mirror of
https://github.com/AuxXxilium/arc-c.git
synced 2024-11-23 15:01:07 +07:00
arc: init reloaded
Signed-off-by: AuxXxilium <info@auxxxilium.tech>
This commit is contained in:
parent
bad838d8b4
commit
2085e367e8
1
.gitattributes
vendored
Normal file
1
.gitattributes
vendored
Normal file
@ -0,0 +1 @@
|
|||||||
|
*.sh eol=lf
|
3
.github/FUNDING.yml
vendored
Normal file
3
.github/FUNDING.yml
vendored
Normal file
@ -0,0 +1,3 @@
|
|||||||
|
# These are supported funding model platforms
|
||||||
|
|
||||||
|
github: AuxXxilium
|
17
.github/ISSUE_TEMPLATE/bug_report.md
vendored
Normal file
17
.github/ISSUE_TEMPLATE/bug_report.md
vendored
Normal file
@ -0,0 +1,17 @@
|
|||||||
|
---
|
||||||
|
name: Bug report
|
||||||
|
about: Create a report to help us improve
|
||||||
|
title: ''
|
||||||
|
labels: bug
|
||||||
|
assignees: ''
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
**Describe the bug**
|
||||||
|
A clear and concise description of what the bug is.
|
||||||
|
|
||||||
|
**Screenshots**
|
||||||
|
If applicable, add screenshots from Arc - Sysinfo menu.
|
||||||
|
|
||||||
|
**Additional context**
|
||||||
|
Add any other context about the problem here.
|
14
.github/ISSUE_TEMPLATE/feature_request.md
vendored
Normal file
14
.github/ISSUE_TEMPLATE/feature_request.md
vendored
Normal file
@ -0,0 +1,14 @@
|
|||||||
|
---
|
||||||
|
name: Feature request
|
||||||
|
about: Suggest an idea for this project
|
||||||
|
title: ''
|
||||||
|
labels: enhancement
|
||||||
|
assignees: ''
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
**Is your feature request related to a problem? Please describe.**
|
||||||
|
A clear and concise description of what the problem is. Ex. I'm always frustrated when [...]
|
||||||
|
|
||||||
|
**Additional context**
|
||||||
|
Add any other context or screenshots about the feature request here.
|
146
.github/workflows/autobuild.yml
vendored
Normal file
146
.github/workflows/autobuild.yml
vendored
Normal file
@ -0,0 +1,146 @@
|
|||||||
|
#
|
||||||
|
# Copyright (C) 2023 AuxXxilium <https://github.com/AuxXxilium> and Ing <https://github.com/wjz304>
|
||||||
|
#
|
||||||
|
# This is free software, licensed under the MIT License.
|
||||||
|
# See /LICENSE for more information.
|
||||||
|
#
|
||||||
|
|
||||||
|
name: Build Arc Dev
|
||||||
|
|
||||||
|
on:
|
||||||
|
workflow_dispatch:
|
||||||
|
push:
|
||||||
|
branches:
|
||||||
|
- dev
|
||||||
|
|
||||||
|
jobs:
|
||||||
|
build:
|
||||||
|
runs-on: ubuntu-latest
|
||||||
|
steps:
|
||||||
|
- name: Checkout
|
||||||
|
uses: actions/checkout@main
|
||||||
|
|
||||||
|
# Install Dependencies
|
||||||
|
- name: Install Dependencies
|
||||||
|
run: |
|
||||||
|
git config --global user.email "info@auxxxilium.tech"
|
||||||
|
git config --global user.name "AuxXxilium"
|
||||||
|
sudo timedatectl set-timezone "Europe/Berlin"
|
||||||
|
|
||||||
|
# Clean up disk space
|
||||||
|
docker rmi $(docker images -q)
|
||||||
|
sudo apt update
|
||||||
|
sudo apt -y purge azure-cli* docker* llvm* google* dotnet* powershell* sphinxsearch* mysql* php*
|
||||||
|
sudo apt -y full-upgrade
|
||||||
|
|
||||||
|
sudo apt update
|
||||||
|
sudo apt install -y jq gawk libelf-dev qemu-utils
|
||||||
|
sudo snap install yq
|
||||||
|
|
||||||
|
# calculates the version number and push
|
||||||
|
- name: Calculate Version
|
||||||
|
run: |
|
||||||
|
# Calculate Version
|
||||||
|
VERSION="`date +'%y.%-m'`.dev"
|
||||||
|
|
||||||
|
echo "Version: ${VERSION}"
|
||||||
|
echo "VERSION=${VERSION}" >> $GITHUB_ENV
|
||||||
|
|
||||||
|
if [ -n "${VERSION}" ]; then
|
||||||
|
# Modify Source File
|
||||||
|
echo "${VERSION}" >VERSION
|
||||||
|
echo "${VERSION}" >files/arc/p1/ARC-VERSION
|
||||||
|
sed 's/^ARC_VERSION=.*/ARC_VERSION="'${VERSION}'"/' -i files/initrd/opt/arc/include/consts.sh
|
||||||
|
fi
|
||||||
|
|
||||||
|
# Get extractor, LKM, Addons, Modules and Configs
|
||||||
|
- name: Get extractor, LKM, Addons, Extensions, Modules and Configs
|
||||||
|
run: |
|
||||||
|
. scripts/func.sh
|
||||||
|
|
||||||
|
getExtractor "files/p3/extractor"
|
||||||
|
getLKMs "files/p3/lkms" ${{ inputs.prerelease }}
|
||||||
|
getAddons "files/p3/addons" ${{ inputs.prerelease }}
|
||||||
|
getExtensions "files/p3/extensions" ${{ inputs.prerelease }}
|
||||||
|
getModules "files/p3/modules" ${{ inputs.prerelease }}
|
||||||
|
getConfigs "files/p3/configs" ${{ inputs.prerelease }}
|
||||||
|
getPatches "files/p3/patches" ${{ inputs.prerelease }}
|
||||||
|
|
||||||
|
echo "OK"
|
||||||
|
|
||||||
|
# Build incremental
|
||||||
|
- name: Build image
|
||||||
|
run: |
|
||||||
|
. scripts/func.sh
|
||||||
|
|
||||||
|
gzip -dc "files/grub.img.gz" >"arc.img"
|
||||||
|
fdisk -l "arc.img"
|
||||||
|
|
||||||
|
LOOPX=$(sudo losetup -f)
|
||||||
|
sudo losetup -P "${LOOPX}" "arc.img"
|
||||||
|
|
||||||
|
echo "Mounting image file"
|
||||||
|
mkdir -p "/tmp/p1"
|
||||||
|
mkdir -p "/tmp/p3"
|
||||||
|
sudo mount ${LOOPX}p1 "/tmp/p1"
|
||||||
|
sudo mount ${LOOPX}p3 "/tmp/p3"
|
||||||
|
|
||||||
|
echo "Get Buildroot"
|
||||||
|
getBuildroot "2023.02.x" "br"
|
||||||
|
[ ! -f "br/bzImage-arc" ] || [ ! -f "br/initrd-arc" ] && return 1
|
||||||
|
|
||||||
|
echo "Repack initrd"
|
||||||
|
cp -f "br/bzImage-arc" "files/p3/bzImage-arc"
|
||||||
|
repackInitrd "br/initrd-arc" "files/initrd" "files/p3/initrd-arc"
|
||||||
|
|
||||||
|
echo "Copying files"
|
||||||
|
sudo cp -Rf "files/p1/"* "/tmp/p1"
|
||||||
|
sudo cp -Rf "files/p3/"* "/tmp/p3"
|
||||||
|
sync
|
||||||
|
|
||||||
|
echo "Unmount image file"
|
||||||
|
sudo umount "/tmp/p1"
|
||||||
|
sudo umount "/tmp/p3"
|
||||||
|
rmdir "/tmp/p1"
|
||||||
|
rmdir "/tmp/p3"
|
||||||
|
|
||||||
|
sudo losetup --detach ${LOOPX}
|
||||||
|
|
||||||
|
# echo "Image Converter"
|
||||||
|
# qemu-img convert -O vmdk arc.img arc-dyn.vmdk
|
||||||
|
# qemu-img convert -O vmdk -o adapter_type=lsilogic arc.img -o subformat=monolithicFlat arc.vmdk
|
||||||
|
|
||||||
|
# Zip image and generate checksum
|
||||||
|
- name: Pack
|
||||||
|
run: |
|
||||||
|
if [ -n "${{ env.VERSION }}" ]; then
|
||||||
|
zip -9 "arc-${{ env.VERSION }}.img.zip" arc.img
|
||||||
|
# zip -9 "arc-${{ env.VERSION }}.vmdk-dyn.zip" arc-dyn.vmdk
|
||||||
|
# zip -9 "arc-${{ env.VERSION }}.vmdk-flat.zip" arc.vmdk arc-flat.vmdk
|
||||||
|
else
|
||||||
|
zip -9 "arc.img.zip" arc.img
|
||||||
|
fi
|
||||||
|
zip -9j update.zip "files/p3/bzImage-arc" "files/p3/initrd-arc" "files/p1/ARC-VERSION" "files/p1/boot/grub/grub.cfg"
|
||||||
|
|
||||||
|
# Upload artifact
|
||||||
|
- name: Upload
|
||||||
|
uses: actions/upload-artifact@v3
|
||||||
|
if: env.VERSION != ''
|
||||||
|
with:
|
||||||
|
name: Images
|
||||||
|
path: |
|
||||||
|
update.zip
|
||||||
|
retention-days: 5
|
||||||
|
|
||||||
|
# Publish a release if is a tag
|
||||||
|
- name: Release
|
||||||
|
uses: softprops/action-gh-release@v1
|
||||||
|
if: env.VERSION != ''
|
||||||
|
with:
|
||||||
|
tag_name: ${{ env.VERSION }}
|
||||||
|
prerelease: ${{ inputs.prerelease }}
|
||||||
|
files: |
|
||||||
|
arc-${{ env.VERSION }}.img.zip
|
||||||
|
arc-${{ env.VERSION }}.vmdk-dyn.zip
|
||||||
|
arc-${{ env.VERSION }}.vmdk-flat.zip
|
||||||
|
update.zip
|
182
.github/workflows/build.yml
vendored
Normal file
182
.github/workflows/build.yml
vendored
Normal file
@ -0,0 +1,182 @@
|
|||||||
|
#
|
||||||
|
# Copyright (C) 2023 AuxXxilium <https://github.com/AuxXxilium> and Ing <https://github.com/wjz304>
|
||||||
|
#
|
||||||
|
# This is free software, licensed under the MIT License.
|
||||||
|
# See /LICENSE for more information.
|
||||||
|
#
|
||||||
|
|
||||||
|
name: Build Arc
|
||||||
|
|
||||||
|
on:
|
||||||
|
workflow_dispatch:
|
||||||
|
inputs:
|
||||||
|
version:
|
||||||
|
description: "format %y.%-m.$i or auto"
|
||||||
|
required: false
|
||||||
|
type: string
|
||||||
|
prerelease:
|
||||||
|
description: "pre release"
|
||||||
|
default: false
|
||||||
|
type: boolean
|
||||||
|
clean:
|
||||||
|
description: "clean"
|
||||||
|
default: false
|
||||||
|
type: boolean
|
||||||
|
|
||||||
|
jobs:
|
||||||
|
build:
|
||||||
|
runs-on: ubuntu-latest
|
||||||
|
steps:
|
||||||
|
- name: Checkout
|
||||||
|
uses: actions/checkout@main
|
||||||
|
|
||||||
|
- name: Clean Old
|
||||||
|
if: inputs.clean == true
|
||||||
|
uses: Nats-ji/delete-old-releases@v1
|
||||||
|
with:
|
||||||
|
token: ${{ secrets.GITHUB_TOKEN }}
|
||||||
|
keep-count: 10
|
||||||
|
keep-old-minor-releases: false
|
||||||
|
|
||||||
|
- name: Changelog
|
||||||
|
uses: Bullrich/generate-release-changelog@master
|
||||||
|
id: Changelog
|
||||||
|
env:
|
||||||
|
REPO: ${{ github.repository }}
|
||||||
|
|
||||||
|
# Install Dependencies
|
||||||
|
- name: Install Dependencies
|
||||||
|
run: |
|
||||||
|
git config --global user.email "info@auxxxilium.tech"
|
||||||
|
git config --global user.name "AuxXxilium"
|
||||||
|
sudo timedatectl set-timezone "Europe/Berlin"
|
||||||
|
|
||||||
|
# Clean up disk space
|
||||||
|
docker rmi $(docker images -q)
|
||||||
|
sudo apt update
|
||||||
|
sudo apt -y purge azure-cli* docker* llvm* google* dotnet* powershell* sphinxsearch* mysql* php*
|
||||||
|
sudo apt -y full-upgrade
|
||||||
|
|
||||||
|
sudo apt update
|
||||||
|
sudo apt install -y jq gawk libelf-dev qemu-utils
|
||||||
|
sudo snap install yq
|
||||||
|
|
||||||
|
# calculates the version number and push
|
||||||
|
- name: Calculate Version
|
||||||
|
run: |
|
||||||
|
# Calculate Version
|
||||||
|
VERSION=""
|
||||||
|
if [ -n "${{ inputs.version }}" ]; then
|
||||||
|
VERSION="${{ inputs.version }}"
|
||||||
|
else
|
||||||
|
LATEST_TAG="$(curl -skL "https://api.github.com/repos/${{ github.repository }}/releases/latest" | jq -r ".tag_name" 2>/dev/null)"
|
||||||
|
if [ -n "${LATEST_TAG}" -a "`echo ${LATEST_TAG} | cut -d '.' -f 1,2`" = "`date +'%y.%-m'`" ]; then # format %y.%-m.$i
|
||||||
|
VERSION="`echo ${LATEST_TAG} | awk -F '.' '{$3=$3+1}1' OFS='.'`"
|
||||||
|
else
|
||||||
|
VERSION="`date +'%y.%-m'`.0"
|
||||||
|
fi
|
||||||
|
fi
|
||||||
|
|
||||||
|
echo "Version: ${VERSION}"
|
||||||
|
echo "VERSION=${VERSION}" >> $GITHUB_ENV
|
||||||
|
|
||||||
|
if [ -n "${VERSION}" ]; then
|
||||||
|
# Modify Source File
|
||||||
|
echo "${VERSION}" >VERSION
|
||||||
|
echo "${VERSION}" >files/arc/p1/ARC-VERSION
|
||||||
|
sed 's/^ARC_VERSION=.*/ARC_VERSION="'${VERSION}'"/' -i files/initrd/opt/arc/include/consts.sh
|
||||||
|
fi
|
||||||
|
|
||||||
|
# Get extractor, LKM, Addons, Modules and Configs
|
||||||
|
- name: Get extractor, LKM, Addons, Extensions, Modules and Configs
|
||||||
|
run: |
|
||||||
|
. scripts/func.sh
|
||||||
|
|
||||||
|
getExtractor "files/p3/extractor"
|
||||||
|
getLKMs "files/p3/lkms" ${{ inputs.prerelease }}
|
||||||
|
getAddons "files/p3/addons" ${{ inputs.prerelease }}
|
||||||
|
getExtensions "files/p3/extensions" ${{ inputs.prerelease }}
|
||||||
|
getModules "files/p3/modules" ${{ inputs.prerelease }}
|
||||||
|
getConfigs "files/p3/configs" ${{ inputs.prerelease }}
|
||||||
|
getPatches "files/p3/patches" ${{ inputs.prerelease }}
|
||||||
|
|
||||||
|
echo "OK"
|
||||||
|
|
||||||
|
# Build incremental
|
||||||
|
- name: Build image
|
||||||
|
run: |
|
||||||
|
. scripts/func.sh
|
||||||
|
|
||||||
|
gzip -dc "files/grub.img.gz" >"arc.img"
|
||||||
|
fdisk -l "arc.img"
|
||||||
|
|
||||||
|
LOOPX=$(sudo losetup -f)
|
||||||
|
sudo losetup -P "${LOOPX}" "arc.img"
|
||||||
|
|
||||||
|
echo "Mounting image file"
|
||||||
|
mkdir -p "/tmp/p1"
|
||||||
|
mkdir -p "/tmp/p3"
|
||||||
|
sudo mount ${LOOPX}p1 "/tmp/p1"
|
||||||
|
sudo mount ${LOOPX}p3 "/tmp/p3"
|
||||||
|
|
||||||
|
echo "Get Buildroot"
|
||||||
|
getBuildroot "2023.02.x" "br"
|
||||||
|
[ ! -f "br/bzImage-arc" ] || [ ! -f "br/initrd-arc" ] && return 1
|
||||||
|
|
||||||
|
echo "Repack initrd"
|
||||||
|
cp -f "br/bzImage-arc" "files/p3/bzImage-arc"
|
||||||
|
repackInitrd "br/initrd-arc" "files/initrd" "files/p3/initrd-arc"
|
||||||
|
|
||||||
|
echo "Copying files"
|
||||||
|
sudo cp -Rf "files/p1/"* "/tmp/p1"
|
||||||
|
sudo cp -Rf "files/p3/"* "/tmp/p3"
|
||||||
|
sync
|
||||||
|
|
||||||
|
echo "Unmount image file"
|
||||||
|
sudo umount "/tmp/p1"
|
||||||
|
sudo umount "/tmp/p3"
|
||||||
|
rmdir "/tmp/p1"
|
||||||
|
rmdir "/tmp/p3"
|
||||||
|
|
||||||
|
sudo losetup --detach ${LOOPX}
|
||||||
|
|
||||||
|
# echo "Image Converter"
|
||||||
|
# qemu-img convert -O vmdk arc.img arc-dyn.vmdk
|
||||||
|
# qemu-img convert -O vmdk -o adapter_type=lsilogic arc.img -o subformat=monolithicFlat arc.vmdk
|
||||||
|
|
||||||
|
# Zip image and generate checksum
|
||||||
|
- name: Pack
|
||||||
|
run: |
|
||||||
|
if [ -n "${{ env.VERSION }}" ]; then
|
||||||
|
zip -9 "arc-${{ env.VERSION }}.img.zip" arc.img
|
||||||
|
# zip -9 "arc-${{ env.VERSION }}.vmdk-dyn.zip" arc-dyn.vmdk
|
||||||
|
# zip -9 "arc-${{ env.VERSION }}.vmdk-flat.zip" arc.vmdk arc-flat.vmdk
|
||||||
|
else
|
||||||
|
zip -9 "arc.img.zip" arc.img
|
||||||
|
fi
|
||||||
|
zip -9j update.zip "files/p3/bzImage-arc" "files/p3/initrd-arc" "files/p1/ARC-VERSION" "files/p1/boot/grub/grub.cfg"
|
||||||
|
|
||||||
|
# Upload artifact
|
||||||
|
- name: Upload
|
||||||
|
uses: actions/upload-artifact@v3
|
||||||
|
if: env.VERSION != ''
|
||||||
|
with:
|
||||||
|
name: Images
|
||||||
|
path: |
|
||||||
|
update.zip
|
||||||
|
retention-days: 5
|
||||||
|
|
||||||
|
# Publish a release if is a tag
|
||||||
|
- name: Release
|
||||||
|
uses: softprops/action-gh-release@v1
|
||||||
|
if: env.VERSION != ''
|
||||||
|
with:
|
||||||
|
tag_name: ${{ env.VERSION }}
|
||||||
|
prerelease: ${{ inputs.prerelease }}
|
||||||
|
body: |
|
||||||
|
${{ steps.Changelog.outputs.changelog }}
|
||||||
|
files: |
|
||||||
|
arc-${{ env.VERSION }}.img.zip
|
||||||
|
arc-${{ env.VERSION }}.vmdk-dyn.zip
|
||||||
|
arc-${{ env.VERSION }}.vmdk-flat.zip
|
||||||
|
update.zip
|
61
.github/workflows/grub.yml
vendored
Normal file
61
.github/workflows/grub.yml
vendored
Normal file
@ -0,0 +1,61 @@
|
|||||||
|
#
|
||||||
|
# Copyright (C) 2023 AuxXxilium <https://github.com/AuxXxilium> and Ing <https://github.com/wjz304>
|
||||||
|
#
|
||||||
|
# This is free software, licensed under the MIT License.
|
||||||
|
# See /LICENSE for more information.
|
||||||
|
#
|
||||||
|
|
||||||
|
name: Grub
|
||||||
|
|
||||||
|
on:
|
||||||
|
workflow_dispatch:
|
||||||
|
inputs:
|
||||||
|
push:
|
||||||
|
description: "push"
|
||||||
|
default: false
|
||||||
|
type: boolean
|
||||||
|
jobs:
|
||||||
|
grub:
|
||||||
|
runs-on: ubuntu-latest
|
||||||
|
steps:
|
||||||
|
- name: Checkout
|
||||||
|
uses: actions/checkout@main
|
||||||
|
|
||||||
|
- name: Initialization environment
|
||||||
|
run : |
|
||||||
|
git config --global user.email "info@auxxxilium.tech"
|
||||||
|
git config --global user.name "AuxXxilium"
|
||||||
|
sudo timedatectl set-timezone "Europe/Berlin"
|
||||||
|
|
||||||
|
sudo apt update
|
||||||
|
sudo apt install -y build-essential bison flex dosfstools
|
||||||
|
|
||||||
|
- name: Make Grub
|
||||||
|
run: |
|
||||||
|
. scripts/grub.sh "grub-2.06" "i386-pc i386-efi x86_64-efi"
|
||||||
|
|
||||||
|
- name: Upload artifact
|
||||||
|
uses: actions/upload-artifact@v3
|
||||||
|
with:
|
||||||
|
name: grub
|
||||||
|
path: |
|
||||||
|
grub.img.gz
|
||||||
|
|
||||||
|
- name: Check and Push
|
||||||
|
if: success() && inputs.push == true
|
||||||
|
run: |
|
||||||
|
if [ -f "grub.img.gz" ]; then
|
||||||
|
echo "Copy img ..."
|
||||||
|
mv -f grub.img.gz files/board/arc/grub.img.gz
|
||||||
|
|
||||||
|
echo "Git push ..."
|
||||||
|
git pull
|
||||||
|
status=$(git status -s | grep -E 'grub.img.gz' | awk '{printf " %s", $2}')
|
||||||
|
if [ -n "${status}" ]; then
|
||||||
|
git add ${status}
|
||||||
|
git commit -m "grub: update $(date +%Y-%m-%d" "%H:%M:%S)"
|
||||||
|
git push -f
|
||||||
|
fi
|
||||||
|
else
|
||||||
|
echo "No change ..."
|
||||||
|
fi
|
20
.gitignore
vendored
20
.gitignore
vendored
@ -1,7 +1,17 @@
|
|||||||
!.gitkeep
|
!.gitkeep
|
||||||
.vscode
|
.vscode
|
||||||
arpl.img*
|
arc.img
|
||||||
/buildroot-2022.02.2
|
arc*.vmdk
|
||||||
test.sh
|
*.zip
|
||||||
docker/Dockerfile
|
.buildroot
|
||||||
docker/cache
|
test*.sh
|
||||||
|
docker/syno-compiler/Dockerfile
|
||||||
|
cache
|
||||||
|
*.bak
|
||||||
|
*.o
|
||||||
|
/files/board/arc/p3/addons
|
||||||
|
/files/board/arc/p3/lkms
|
||||||
|
/files/board/arc/p3/modules
|
||||||
|
/files/board/arc/p3/configs
|
||||||
|
/files/board/arc/p3/extensions
|
||||||
|
/tests
|
674
LICENSE
Normal file
674
LICENSE
Normal file
@ -0,0 +1,674 @@
|
|||||||
|
GNU GENERAL PUBLIC LICENSE
|
||||||
|
Version 3, 29 June 2007
|
||||||
|
|
||||||
|
Copyright (C) 2007 Free Software Foundation, Inc. <https://fsf.org/>
|
||||||
|
Everyone is permitted to copy and distribute verbatim copies
|
||||||
|
of this license document, but changing it is not allowed.
|
||||||
|
|
||||||
|
Preamble
|
||||||
|
|
||||||
|
The GNU General Public License is a free, copyleft license for
|
||||||
|
software and other kinds of works.
|
||||||
|
|
||||||
|
The licenses for most software and other practical works are designed
|
||||||
|
to take away your freedom to share and change the works. By contrast,
|
||||||
|
the GNU General Public License is intended to guarantee your freedom to
|
||||||
|
share and change all versions of a program--to make sure it remains free
|
||||||
|
software for all its users. We, the Free Software Foundation, use the
|
||||||
|
GNU General Public License for most of our software; it applies also to
|
||||||
|
any other work released this way by its authors. You can apply it to
|
||||||
|
your programs, too.
|
||||||
|
|
||||||
|
When we speak of free software, we are referring to freedom, not
|
||||||
|
price. Our General Public Licenses are designed to make sure that you
|
||||||
|
have the freedom to distribute copies of free software (and charge for
|
||||||
|
them if you wish), that you receive source code or can get it if you
|
||||||
|
want it, that you can change the software or use pieces of it in new
|
||||||
|
free programs, and that you know you can do these things.
|
||||||
|
|
||||||
|
To protect your rights, we need to prevent others from denying you
|
||||||
|
these rights or asking you to surrender the rights. Therefore, you have
|
||||||
|
certain responsibilities if you distribute copies of the software, or if
|
||||||
|
you modify it: responsibilities to respect the freedom of others.
|
||||||
|
|
||||||
|
For example, if you distribute copies of such a program, whether
|
||||||
|
gratis or for a fee, you must pass on to the recipients the same
|
||||||
|
freedoms that you received. You must make sure that they, too, receive
|
||||||
|
or can get the source code. And you must show them these terms so they
|
||||||
|
know their rights.
|
||||||
|
|
||||||
|
Developers that use the GNU GPL protect your rights with two steps:
|
||||||
|
(1) assert copyright on the software, and (2) offer you this License
|
||||||
|
giving you legal permission to copy, distribute and/or modify it.
|
||||||
|
|
||||||
|
For the developers' and authors' protection, the GPL clearly explains
|
||||||
|
that there is no warranty for this free software. For both users' and
|
||||||
|
authors' sake, the GPL requires that modified versions be marked as
|
||||||
|
changed, so that their problems will not be attributed erroneously to
|
||||||
|
authors of previous versions.
|
||||||
|
|
||||||
|
Some devices are designed to deny users access to install or run
|
||||||
|
modified versions of the software inside them, although the manufacturer
|
||||||
|
can do so. This is fundamentally incompatible with the aim of
|
||||||
|
protecting users' freedom to change the software. The systematic
|
||||||
|
pattern of such abuse occurs in the area of products for individuals to
|
||||||
|
use, which is precisely where it is most unacceptable. Therefore, we
|
||||||
|
have designed this version of the GPL to prohibit the practice for those
|
||||||
|
products. If such problems arise substantially in other domains, we
|
||||||
|
stand ready to extend this provision to those domains in future versions
|
||||||
|
of the GPL, as needed to protect the freedom of users.
|
||||||
|
|
||||||
|
Finally, every program is threatened constantly by software patents.
|
||||||
|
States should not allow patents to restrict development and use of
|
||||||
|
software on general-purpose computers, but in those that do, we wish to
|
||||||
|
avoid the special danger that patents applied to a free program could
|
||||||
|
make it effectively proprietary. To prevent this, the GPL assures that
|
||||||
|
patents cannot be used to render the program non-free.
|
||||||
|
|
||||||
|
The precise terms and conditions for copying, distribution and
|
||||||
|
modification follow.
|
||||||
|
|
||||||
|
TERMS AND CONDITIONS
|
||||||
|
|
||||||
|
0. Definitions.
|
||||||
|
|
||||||
|
"This License" refers to version 3 of the GNU General Public License.
|
||||||
|
|
||||||
|
"Copyright" also means copyright-like laws that apply to other kinds of
|
||||||
|
works, such as semiconductor masks.
|
||||||
|
|
||||||
|
"The Program" refers to any copyrightable work licensed under this
|
||||||
|
License. Each licensee is addressed as "you". "Licensees" and
|
||||||
|
"recipients" may be individuals or organizations.
|
||||||
|
|
||||||
|
To "modify" a work means to copy from or adapt all or part of the work
|
||||||
|
in a fashion requiring copyright permission, other than the making of an
|
||||||
|
exact copy. The resulting work is called a "modified version" of the
|
||||||
|
earlier work or a work "based on" the earlier work.
|
||||||
|
|
||||||
|
A "covered work" means either the unmodified Program or a work based
|
||||||
|
on the Program.
|
||||||
|
|
||||||
|
To "propagate" a work means to do anything with it that, without
|
||||||
|
permission, would make you directly or secondarily liable for
|
||||||
|
infringement under applicable copyright law, except executing it on a
|
||||||
|
computer or modifying a private copy. Propagation includes copying,
|
||||||
|
distribution (with or without modification), making available to the
|
||||||
|
public, and in some countries other activities as well.
|
||||||
|
|
||||||
|
To "convey" a work means any kind of propagation that enables other
|
||||||
|
parties to make or receive copies. Mere interaction with a user through
|
||||||
|
a computer network, with no transfer of a copy, is not conveying.
|
||||||
|
|
||||||
|
An interactive user interface displays "Appropriate Legal Notices"
|
||||||
|
to the extent that it includes a convenient and prominently visible
|
||||||
|
feature that (1) displays an appropriate copyright notice, and (2)
|
||||||
|
tells the user that there is no warranty for the work (except to the
|
||||||
|
extent that warranties are provided), that licensees may convey the
|
||||||
|
work under this License, and how to view a copy of this License. If
|
||||||
|
the interface presents a list of user commands or options, such as a
|
||||||
|
menu, a prominent item in the list meets this criterion.
|
||||||
|
|
||||||
|
1. Source Code.
|
||||||
|
|
||||||
|
The "source code" for a work means the preferred form of the work
|
||||||
|
for making modifications to it. "Object code" means any non-source
|
||||||
|
form of a work.
|
||||||
|
|
||||||
|
A "Standard Interface" means an interface that either is an official
|
||||||
|
standard defined by a recognized standards body, or, in the case of
|
||||||
|
interfaces specified for a particular programming language, one that
|
||||||
|
is widely used among developers working in that language.
|
||||||
|
|
||||||
|
The "System Libraries" of an executable work include anything, other
|
||||||
|
than the work as a whole, that (a) is included in the normal form of
|
||||||
|
packaging a Major Component, but which is not part of that Major
|
||||||
|
Component, and (b) serves only to enable use of the work with that
|
||||||
|
Major Component, or to implement a Standard Interface for which an
|
||||||
|
implementation is available to the public in source code form. A
|
||||||
|
"Major Component", in this context, means a major essential component
|
||||||
|
(kernel, window system, and so on) of the specific operating system
|
||||||
|
(if any) on which the executable work runs, or a compiler used to
|
||||||
|
produce the work, or an object code interpreter used to run it.
|
||||||
|
|
||||||
|
The "Corresponding Source" for a work in object code form means all
|
||||||
|
the source code needed to generate, install, and (for an executable
|
||||||
|
work) run the object code and to modify the work, including scripts to
|
||||||
|
control those activities. However, it does not include the work's
|
||||||
|
System Libraries, or general-purpose tools or generally available free
|
||||||
|
programs which are used unmodified in performing those activities but
|
||||||
|
which are not part of the work. For example, Corresponding Source
|
||||||
|
includes interface definition files associated with source files for
|
||||||
|
the work, and the source code for shared libraries and dynamically
|
||||||
|
linked subprograms that the work is specifically designed to require,
|
||||||
|
such as by intimate data communication or control flow between those
|
||||||
|
subprograms and other parts of the work.
|
||||||
|
|
||||||
|
The Corresponding Source need not include anything that users
|
||||||
|
can regenerate automatically from other parts of the Corresponding
|
||||||
|
Source.
|
||||||
|
|
||||||
|
The Corresponding Source for a work in source code form is that
|
||||||
|
same work.
|
||||||
|
|
||||||
|
2. Basic Permissions.
|
||||||
|
|
||||||
|
All rights granted under this License are granted for the term of
|
||||||
|
copyright on the Program, and are irrevocable provided the stated
|
||||||
|
conditions are met. This License explicitly affirms your unlimited
|
||||||
|
permission to run the unmodified Program. The output from running a
|
||||||
|
covered work is covered by this License only if the output, given its
|
||||||
|
content, constitutes a covered work. This License acknowledges your
|
||||||
|
rights of fair use or other equivalent, as provided by copyright law.
|
||||||
|
|
||||||
|
You may make, run and propagate covered works that you do not
|
||||||
|
convey, without conditions so long as your license otherwise remains
|
||||||
|
in force. You may convey covered works to others for the sole purpose
|
||||||
|
of having them make modifications exclusively for you, or provide you
|
||||||
|
with facilities for running those works, provided that you comply with
|
||||||
|
the terms of this License in conveying all material for which you do
|
||||||
|
not control copyright. Those thus making or running the covered works
|
||||||
|
for you must do so exclusively on your behalf, under your direction
|
||||||
|
and control, on terms that prohibit them from making any copies of
|
||||||
|
your copyrighted material outside their relationship with you.
|
||||||
|
|
||||||
|
Conveying under any other circumstances is permitted solely under
|
||||||
|
the conditions stated below. Sublicensing is not allowed; section 10
|
||||||
|
makes it unnecessary.
|
||||||
|
|
||||||
|
3. Protecting Users' Legal Rights From Anti-Circumvention Law.
|
||||||
|
|
||||||
|
No covered work shall be deemed part of an effective technological
|
||||||
|
measure under any applicable law fulfilling obligations under article
|
||||||
|
11 of the WIPO copyright treaty adopted on 20 December 1996, or
|
||||||
|
similar laws prohibiting or restricting circumvention of such
|
||||||
|
measures.
|
||||||
|
|
||||||
|
When you convey a covered work, you waive any legal power to forbid
|
||||||
|
circumvention of technological measures to the extent such circumvention
|
||||||
|
is effected by exercising rights under this License with respect to
|
||||||
|
the covered work, and you disclaim any intention to limit operation or
|
||||||
|
modification of the work as a means of enforcing, against the work's
|
||||||
|
users, your or third parties' legal rights to forbid circumvention of
|
||||||
|
technological measures.
|
||||||
|
|
||||||
|
4. Conveying Verbatim Copies.
|
||||||
|
|
||||||
|
You may convey verbatim copies of the Program's source code as you
|
||||||
|
receive it, in any medium, provided that you conspicuously and
|
||||||
|
appropriately publish on each copy an appropriate copyright notice;
|
||||||
|
keep intact all notices stating that this License and any
|
||||||
|
non-permissive terms added in accord with section 7 apply to the code;
|
||||||
|
keep intact all notices of the absence of any warranty; and give all
|
||||||
|
recipients a copy of this License along with the Program.
|
||||||
|
|
||||||
|
You may charge any price or no price for each copy that you convey,
|
||||||
|
and you may offer support or warranty protection for a fee.
|
||||||
|
|
||||||
|
5. Conveying Modified Source Versions.
|
||||||
|
|
||||||
|
You may convey a work based on the Program, or the modifications to
|
||||||
|
produce it from the Program, in the form of source code under the
|
||||||
|
terms of section 4, provided that you also meet all of these conditions:
|
||||||
|
|
||||||
|
a) The work must carry prominent notices stating that you modified
|
||||||
|
it, and giving a relevant date.
|
||||||
|
|
||||||
|
b) The work must carry prominent notices stating that it is
|
||||||
|
released under this License and any conditions added under section
|
||||||
|
7. This requirement modifies the requirement in section 4 to
|
||||||
|
"keep intact all notices".
|
||||||
|
|
||||||
|
c) You must license the entire work, as a whole, under this
|
||||||
|
License to anyone who comes into possession of a copy. This
|
||||||
|
License will therefore apply, along with any applicable section 7
|
||||||
|
additional terms, to the whole of the work, and all its parts,
|
||||||
|
regardless of how they are packaged. This License gives no
|
||||||
|
permission to license the work in any other way, but it does not
|
||||||
|
invalidate such permission if you have separately received it.
|
||||||
|
|
||||||
|
d) If the work has interactive user interfaces, each must display
|
||||||
|
Appropriate Legal Notices; however, if the Program has interactive
|
||||||
|
interfaces that do not display Appropriate Legal Notices, your
|
||||||
|
work need not make them do so.
|
||||||
|
|
||||||
|
A compilation of a covered work with other separate and independent
|
||||||
|
works, which are not by their nature extensions of the covered work,
|
||||||
|
and which are not combined with it such as to form a larger program,
|
||||||
|
in or on a volume of a storage or distribution medium, is called an
|
||||||
|
"aggregate" if the compilation and its resulting copyright are not
|
||||||
|
used to limit the access or legal rights of the compilation's users
|
||||||
|
beyond what the individual works permit. Inclusion of a covered work
|
||||||
|
in an aggregate does not cause this License to apply to the other
|
||||||
|
parts of the aggregate.
|
||||||
|
|
||||||
|
6. Conveying Non-Source Forms.
|
||||||
|
|
||||||
|
You may convey a covered work in object code form under the terms
|
||||||
|
of sections 4 and 5, provided that you also convey the
|
||||||
|
machine-readable Corresponding Source under the terms of this License,
|
||||||
|
in one of these ways:
|
||||||
|
|
||||||
|
a) Convey the object code in, or embodied in, a physical product
|
||||||
|
(including a physical distribution medium), accompanied by the
|
||||||
|
Corresponding Source fixed on a durable physical medium
|
||||||
|
customarily used for software interchange.
|
||||||
|
|
||||||
|
b) Convey the object code in, or embodied in, a physical product
|
||||||
|
(including a physical distribution medium), accompanied by a
|
||||||
|
written offer, valid for at least three years and valid for as
|
||||||
|
long as you offer spare parts or customer support for that product
|
||||||
|
model, to give anyone who possesses the object code either (1) a
|
||||||
|
copy of the Corresponding Source for all the software in the
|
||||||
|
product that is covered by this License, on a durable physical
|
||||||
|
medium customarily used for software interchange, for a price no
|
||||||
|
more than your reasonable cost of physically performing this
|
||||||
|
conveying of source, or (2) access to copy the
|
||||||
|
Corresponding Source from a network server at no charge.
|
||||||
|
|
||||||
|
c) Convey individual copies of the object code with a copy of the
|
||||||
|
written offer to provide the Corresponding Source. This
|
||||||
|
alternative is allowed only occasionally and noncommercially, and
|
||||||
|
only if you received the object code with such an offer, in accord
|
||||||
|
with subsection 6b.
|
||||||
|
|
||||||
|
d) Convey the object code by offering access from a designated
|
||||||
|
place (gratis or for a charge), and offer equivalent access to the
|
||||||
|
Corresponding Source in the same way through the same place at no
|
||||||
|
further charge. You need not require recipients to copy the
|
||||||
|
Corresponding Source along with the object code. If the place to
|
||||||
|
copy the object code is a network server, the Corresponding Source
|
||||||
|
may be on a different server (operated by you or a third party)
|
||||||
|
that supports equivalent copying facilities, provided you maintain
|
||||||
|
clear directions next to the object code saying where to find the
|
||||||
|
Corresponding Source. Regardless of what server hosts the
|
||||||
|
Corresponding Source, you remain obligated to ensure that it is
|
||||||
|
available for as long as needed to satisfy these requirements.
|
||||||
|
|
||||||
|
e) Convey the object code using peer-to-peer transmission, provided
|
||||||
|
you inform other peers where the object code and Corresponding
|
||||||
|
Source of the work are being offered to the general public at no
|
||||||
|
charge under subsection 6d.
|
||||||
|
|
||||||
|
A separable portion of the object code, whose source code is excluded
|
||||||
|
from the Corresponding Source as a System Library, need not be
|
||||||
|
included in conveying the object code work.
|
||||||
|
|
||||||
|
A "User Product" is either (1) a "consumer product", which means any
|
||||||
|
tangible personal property which is normally used for personal, family,
|
||||||
|
or household purposes, or (2) anything designed or sold for incorporation
|
||||||
|
into a dwelling. In determining whether a product is a consumer product,
|
||||||
|
doubtful cases shall be resolved in favor of coverage. For a particular
|
||||||
|
product received by a particular user, "normally used" refers to a
|
||||||
|
typical or common use of that class of product, regardless of the status
|
||||||
|
of the particular user or of the way in which the particular user
|
||||||
|
actually uses, or expects or is expected to use, the product. A product
|
||||||
|
is a consumer product regardless of whether the product has substantial
|
||||||
|
commercial, industrial or non-consumer uses, unless such uses represent
|
||||||
|
the only significant mode of use of the product.
|
||||||
|
|
||||||
|
"Installation Information" for a User Product means any methods,
|
||||||
|
procedures, authorization keys, or other information required to install
|
||||||
|
and execute modified versions of a covered work in that User Product from
|
||||||
|
a modified version of its Corresponding Source. The information must
|
||||||
|
suffice to ensure that the continued functioning of the modified object
|
||||||
|
code is in no case prevented or interfered with solely because
|
||||||
|
modification has been made.
|
||||||
|
|
||||||
|
If you convey an object code work under this section in, or with, or
|
||||||
|
specifically for use in, a User Product, and the conveying occurs as
|
||||||
|
part of a transaction in which the right of possession and use of the
|
||||||
|
User Product is transferred to the recipient in perpetuity or for a
|
||||||
|
fixed term (regardless of how the transaction is characterized), the
|
||||||
|
Corresponding Source conveyed under this section must be accompanied
|
||||||
|
by the Installation Information. But this requirement does not apply
|
||||||
|
if neither you nor any third party retains the ability to install
|
||||||
|
modified object code on the User Product (for example, the work has
|
||||||
|
been installed in ROM).
|
||||||
|
|
||||||
|
The requirement to provide Installation Information does not include a
|
||||||
|
requirement to continue to provide support service, warranty, or updates
|
||||||
|
for a work that has been modified or installed by the recipient, or for
|
||||||
|
the User Product in which it has been modified or installed. Access to a
|
||||||
|
network may be denied when the modification itself materially and
|
||||||
|
adversely affects the operation of the network or violates the rules and
|
||||||
|
protocols for communication across the network.
|
||||||
|
|
||||||
|
Corresponding Source conveyed, and Installation Information provided,
|
||||||
|
in accord with this section must be in a format that is publicly
|
||||||
|
documented (and with an implementation available to the public in
|
||||||
|
source code form), and must require no special password or key for
|
||||||
|
unpacking, reading or copying.
|
||||||
|
|
||||||
|
7. Additional Terms.
|
||||||
|
|
||||||
|
"Additional permissions" are terms that supplement the terms of this
|
||||||
|
License by making exceptions from one or more of its conditions.
|
||||||
|
Additional permissions that are applicable to the entire Program shall
|
||||||
|
be treated as though they were included in this License, to the extent
|
||||||
|
that they are valid under applicable law. If additional permissions
|
||||||
|
apply only to part of the Program, that part may be used separately
|
||||||
|
under those permissions, but the entire Program remains governed by
|
||||||
|
this License without regard to the additional permissions.
|
||||||
|
|
||||||
|
When you convey a copy of a covered work, you may at your option
|
||||||
|
remove any additional permissions from that copy, or from any part of
|
||||||
|
it. (Additional permissions may be written to require their own
|
||||||
|
removal in certain cases when you modify the work.) You may place
|
||||||
|
additional permissions on material, added by you to a covered work,
|
||||||
|
for which you have or can give appropriate copyright permission.
|
||||||
|
|
||||||
|
Notwithstanding any other provision of this License, for material you
|
||||||
|
add to a covered work, you may (if authorized by the copyright holders of
|
||||||
|
that material) supplement the terms of this License with terms:
|
||||||
|
|
||||||
|
a) Disclaiming warranty or limiting liability differently from the
|
||||||
|
terms of sections 15 and 16 of this License; or
|
||||||
|
|
||||||
|
b) Requiring preservation of specified reasonable legal notices or
|
||||||
|
author attributions in that material or in the Appropriate Legal
|
||||||
|
Notices displayed by works containing it; or
|
||||||
|
|
||||||
|
c) Prohibiting misrepresentation of the origin of that material, or
|
||||||
|
requiring that modified versions of such material be marked in
|
||||||
|
reasonable ways as different from the original version; or
|
||||||
|
|
||||||
|
d) Limiting the use for publicity purposes of names of licensors or
|
||||||
|
authors of the material; or
|
||||||
|
|
||||||
|
e) Declining to grant rights under trademark law for use of some
|
||||||
|
trade names, trademarks, or service marks; or
|
||||||
|
|
||||||
|
f) Requiring indemnification of licensors and authors of that
|
||||||
|
material by anyone who conveys the material (or modified versions of
|
||||||
|
it) with contractual assumptions of liability to the recipient, for
|
||||||
|
any liability that these contractual assumptions directly impose on
|
||||||
|
those licensors and authors.
|
||||||
|
|
||||||
|
All other non-permissive additional terms are considered "further
|
||||||
|
restrictions" within the meaning of section 10. If the Program as you
|
||||||
|
received it, or any part of it, contains a notice stating that it is
|
||||||
|
governed by this License along with a term that is a further
|
||||||
|
restriction, you may remove that term. If a license document contains
|
||||||
|
a further restriction but permits relicensing or conveying under this
|
||||||
|
License, you may add to a covered work material governed by the terms
|
||||||
|
of that license document, provided that the further restriction does
|
||||||
|
not survive such relicensing or conveying.
|
||||||
|
|
||||||
|
If you add terms to a covered work in accord with this section, you
|
||||||
|
must place, in the relevant source files, a statement of the
|
||||||
|
additional terms that apply to those files, or a notice indicating
|
||||||
|
where to find the applicable terms.
|
||||||
|
|
||||||
|
Additional terms, permissive or non-permissive, may be stated in the
|
||||||
|
form of a separately written license, or stated as exceptions;
|
||||||
|
the above requirements apply either way.
|
||||||
|
|
||||||
|
8. Termination.
|
||||||
|
|
||||||
|
You may not propagate or modify a covered work except as expressly
|
||||||
|
provided under this License. Any attempt otherwise to propagate or
|
||||||
|
modify it is void, and will automatically terminate your rights under
|
||||||
|
this License (including any patent licenses granted under the third
|
||||||
|
paragraph of section 11).
|
||||||
|
|
||||||
|
However, if you cease all violation of this License, then your
|
||||||
|
license from a particular copyright holder is reinstated (a)
|
||||||
|
provisionally, unless and until the copyright holder explicitly and
|
||||||
|
finally terminates your license, and (b) permanently, if the copyright
|
||||||
|
holder fails to notify you of the violation by some reasonable means
|
||||||
|
prior to 60 days after the cessation.
|
||||||
|
|
||||||
|
Moreover, your license from a particular copyright holder is
|
||||||
|
reinstated permanently if the copyright holder notifies you of the
|
||||||
|
violation by some reasonable means, this is the first time you have
|
||||||
|
received notice of violation of this License (for any work) from that
|
||||||
|
copyright holder, and you cure the violation prior to 30 days after
|
||||||
|
your receipt of the notice.
|
||||||
|
|
||||||
|
Termination of your rights under this section does not terminate the
|
||||||
|
licenses of parties who have received copies or rights from you under
|
||||||
|
this License. If your rights have been terminated and not permanently
|
||||||
|
reinstated, you do not qualify to receive new licenses for the same
|
||||||
|
material under section 10.
|
||||||
|
|
||||||
|
9. Acceptance Not Required for Having Copies.
|
||||||
|
|
||||||
|
You are not required to accept this License in order to receive or
|
||||||
|
run a copy of the Program. Ancillary propagation of a covered work
|
||||||
|
occurring solely as a consequence of using peer-to-peer transmission
|
||||||
|
to receive a copy likewise does not require acceptance. However,
|
||||||
|
nothing other than this License grants you permission to propagate or
|
||||||
|
modify any covered work. These actions infringe copyright if you do
|
||||||
|
not accept this License. Therefore, by modifying or propagating a
|
||||||
|
covered work, you indicate your acceptance of this License to do so.
|
||||||
|
|
||||||
|
10. Automatic Licensing of Downstream Recipients.
|
||||||
|
|
||||||
|
Each time you convey a covered work, the recipient automatically
|
||||||
|
receives a license from the original licensors, to run, modify and
|
||||||
|
propagate that work, subject to this License. You are not responsible
|
||||||
|
for enforcing compliance by third parties with this License.
|
||||||
|
|
||||||
|
An "entity transaction" is a transaction transferring control of an
|
||||||
|
organization, or substantially all assets of one, or subdividing an
|
||||||
|
organization, or merging organizations. If propagation of a covered
|
||||||
|
work results from an entity transaction, each party to that
|
||||||
|
transaction who receives a copy of the work also receives whatever
|
||||||
|
licenses to the work the party's predecessor in interest had or could
|
||||||
|
give under the previous paragraph, plus a right to possession of the
|
||||||
|
Corresponding Source of the work from the predecessor in interest, if
|
||||||
|
the predecessor has it or can get it with reasonable efforts.
|
||||||
|
|
||||||
|
You may not impose any further restrictions on the exercise of the
|
||||||
|
rights granted or affirmed under this License. For example, you may
|
||||||
|
not impose a license fee, royalty, or other charge for exercise of
|
||||||
|
rights granted under this License, and you may not initiate litigation
|
||||||
|
(including a cross-claim or counterclaim in a lawsuit) alleging that
|
||||||
|
any patent claim is infringed by making, using, selling, offering for
|
||||||
|
sale, or importing the Program or any portion of it.
|
||||||
|
|
||||||
|
11. Patents.
|
||||||
|
|
||||||
|
A "contributor" is a copyright holder who authorizes use under this
|
||||||
|
License of the Program or a work on which the Program is based. The
|
||||||
|
work thus licensed is called the contributor's "contributor version".
|
||||||
|
|
||||||
|
A contributor's "essential patent claims" are all patent claims
|
||||||
|
owned or controlled by the contributor, whether already acquired or
|
||||||
|
hereafter acquired, that would be infringed by some manner, permitted
|
||||||
|
by this License, of making, using, or selling its contributor version,
|
||||||
|
but do not include claims that would be infringed only as a
|
||||||
|
consequence of further modification of the contributor version. For
|
||||||
|
purposes of this definition, "control" includes the right to grant
|
||||||
|
patent sublicenses in a manner consistent with the requirements of
|
||||||
|
this License.
|
||||||
|
|
||||||
|
Each contributor grants you a non-exclusive, worldwide, royalty-free
|
||||||
|
patent license under the contributor's essential patent claims, to
|
||||||
|
make, use, sell, offer for sale, import and otherwise run, modify and
|
||||||
|
propagate the contents of its contributor version.
|
||||||
|
|
||||||
|
In the following three paragraphs, a "patent license" is any express
|
||||||
|
agreement or commitment, however denominated, not to enforce a patent
|
||||||
|
(such as an express permission to practice a patent or covenant not to
|
||||||
|
sue for patent infringement). To "grant" such a patent license to a
|
||||||
|
party means to make such an agreement or commitment not to enforce a
|
||||||
|
patent against the party.
|
||||||
|
|
||||||
|
If you convey a covered work, knowingly relying on a patent license,
|
||||||
|
and the Corresponding Source of the work is not available for anyone
|
||||||
|
to copy, free of charge and under the terms of this License, through a
|
||||||
|
publicly available network server or other readily accessible means,
|
||||||
|
then you must either (1) cause the Corresponding Source to be so
|
||||||
|
available, or (2) arrange to deprive yourself of the benefit of the
|
||||||
|
patent license for this particular work, or (3) arrange, in a manner
|
||||||
|
consistent with the requirements of this License, to extend the patent
|
||||||
|
license to downstream recipients. "Knowingly relying" means you have
|
||||||
|
actual knowledge that, but for the patent license, your conveying the
|
||||||
|
covered work in a country, or your recipient's use of the covered work
|
||||||
|
in a country, would infringe one or more identifiable patents in that
|
||||||
|
country that you have reason to believe are valid.
|
||||||
|
|
||||||
|
If, pursuant to or in connection with a single transaction or
|
||||||
|
arrangement, you convey, or propagate by procuring conveyance of, a
|
||||||
|
covered work, and grant a patent license to some of the parties
|
||||||
|
receiving the covered work authorizing them to use, propagate, modify
|
||||||
|
or convey a specific copy of the covered work, then the patent license
|
||||||
|
you grant is automatically extended to all recipients of the covered
|
||||||
|
work and works based on it.
|
||||||
|
|
||||||
|
A patent license is "discriminatory" if it does not include within
|
||||||
|
the scope of its coverage, prohibits the exercise of, or is
|
||||||
|
conditioned on the non-exercise of one or more of the rights that are
|
||||||
|
specifically granted under this License. You may not convey a covered
|
||||||
|
work if you are a party to an arrangement with a third party that is
|
||||||
|
in the business of distributing software, under which you make payment
|
||||||
|
to the third party based on the extent of your activity of conveying
|
||||||
|
the work, and under which the third party grants, to any of the
|
||||||
|
parties who would receive the covered work from you, a discriminatory
|
||||||
|
patent license (a) in connection with copies of the covered work
|
||||||
|
conveyed by you (or copies made from those copies), or (b) primarily
|
||||||
|
for and in connection with specific products or compilations that
|
||||||
|
contain the covered work, unless you entered into that arrangement,
|
||||||
|
or that patent license was granted, prior to 28 March 2007.
|
||||||
|
|
||||||
|
Nothing in this License shall be construed as excluding or limiting
|
||||||
|
any implied license or other defenses to infringement that may
|
||||||
|
otherwise be available to you under applicable patent law.
|
||||||
|
|
||||||
|
12. No Surrender of Others' Freedom.
|
||||||
|
|
||||||
|
If conditions are imposed on you (whether by court order, agreement or
|
||||||
|
otherwise) that contradict the conditions of this License, they do not
|
||||||
|
excuse you from the conditions of this License. If you cannot convey a
|
||||||
|
covered work so as to satisfy simultaneously your obligations under this
|
||||||
|
License and any other pertinent obligations, then as a consequence you may
|
||||||
|
not convey it at all. For example, if you agree to terms that obligate you
|
||||||
|
to collect a royalty for further conveying from those to whom you convey
|
||||||
|
the Program, the only way you could satisfy both those terms and this
|
||||||
|
License would be to refrain entirely from conveying the Program.
|
||||||
|
|
||||||
|
13. Use with the GNU Affero General Public License.
|
||||||
|
|
||||||
|
Notwithstanding any other provision of this License, you have
|
||||||
|
permission to link or combine any covered work with a work licensed
|
||||||
|
under version 3 of the GNU Affero General Public License into a single
|
||||||
|
combined work, and to convey the resulting work. The terms of this
|
||||||
|
License will continue to apply to the part which is the covered work,
|
||||||
|
but the special requirements of the GNU Affero General Public License,
|
||||||
|
section 13, concerning interaction through a network will apply to the
|
||||||
|
combination as such.
|
||||||
|
|
||||||
|
14. Revised Versions of this License.
|
||||||
|
|
||||||
|
The Free Software Foundation may publish revised and/or new versions of
|
||||||
|
the GNU General Public License from time to time. Such new versions will
|
||||||
|
be similar in spirit to the present version, but may differ in detail to
|
||||||
|
address new problems or concerns.
|
||||||
|
|
||||||
|
Each version is given a distinguishing version number. If the
|
||||||
|
Program specifies that a certain numbered version of the GNU General
|
||||||
|
Public License "or any later version" applies to it, you have the
|
||||||
|
option of following the terms and conditions either of that numbered
|
||||||
|
version or of any later version published by the Free Software
|
||||||
|
Foundation. If the Program does not specify a version number of the
|
||||||
|
GNU General Public License, you may choose any version ever published
|
||||||
|
by the Free Software Foundation.
|
||||||
|
|
||||||
|
If the Program specifies that a proxy can decide which future
|
||||||
|
versions of the GNU General Public License can be used, that proxy's
|
||||||
|
public statement of acceptance of a version permanently authorizes you
|
||||||
|
to choose that version for the Program.
|
||||||
|
|
||||||
|
Later license versions may give you additional or different
|
||||||
|
permissions. However, no additional obligations are imposed on any
|
||||||
|
author or copyright holder as a result of your choosing to follow a
|
||||||
|
later version.
|
||||||
|
|
||||||
|
15. Disclaimer of Warranty.
|
||||||
|
|
||||||
|
THERE IS NO WARRANTY FOR THE PROGRAM, TO THE EXTENT PERMITTED BY
|
||||||
|
APPLICABLE LAW. EXCEPT WHEN OTHERWISE STATED IN WRITING THE COPYRIGHT
|
||||||
|
HOLDERS AND/OR OTHER PARTIES PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY
|
||||||
|
OF ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING, BUT NOT LIMITED TO,
|
||||||
|
THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
|
||||||
|
PURPOSE. THE ENTIRE RISK AS TO THE QUALITY AND PERFORMANCE OF THE PROGRAM
|
||||||
|
IS WITH YOU. SHOULD THE PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF
|
||||||
|
ALL NECESSARY SERVICING, REPAIR OR CORRECTION.
|
||||||
|
|
||||||
|
16. Limitation of Liability.
|
||||||
|
|
||||||
|
IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING
|
||||||
|
WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MODIFIES AND/OR CONVEYS
|
||||||
|
THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES, INCLUDING ANY
|
||||||
|
GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING OUT OF THE
|
||||||
|
USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED TO LOSS OF
|
||||||
|
DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY YOU OR THIRD
|
||||||
|
PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER PROGRAMS),
|
||||||
|
EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE POSSIBILITY OF
|
||||||
|
SUCH DAMAGES.
|
||||||
|
|
||||||
|
17. Interpretation of Sections 15 and 16.
|
||||||
|
|
||||||
|
If the disclaimer of warranty and limitation of liability provided
|
||||||
|
above cannot be given local legal effect according to their terms,
|
||||||
|
reviewing courts shall apply local law that most closely approximates
|
||||||
|
an absolute waiver of all civil liability in connection with the
|
||||||
|
Program, unless a warranty or assumption of liability accompanies a
|
||||||
|
copy of the Program in return for a fee.
|
||||||
|
|
||||||
|
END OF TERMS AND CONDITIONS
|
||||||
|
|
||||||
|
How to Apply These Terms to Your New Programs
|
||||||
|
|
||||||
|
If you develop a new program, and you want it to be of the greatest
|
||||||
|
possible use to the public, the best way to achieve this is to make it
|
||||||
|
free software which everyone can redistribute and change under these terms.
|
||||||
|
|
||||||
|
To do so, attach the following notices to the program. It is safest
|
||||||
|
to attach them to the start of each source file to most effectively
|
||||||
|
state the exclusion of warranty; and each file should have at least
|
||||||
|
the "copyright" line and a pointer to where the full notice is found.
|
||||||
|
|
||||||
|
<one line to give the program's name and a brief idea of what it does.>
|
||||||
|
Copyright (C) <year> <name of author>
|
||||||
|
|
||||||
|
This program is free software: you can redistribute it and/or modify
|
||||||
|
it under the terms of the GNU General Public License as published by
|
||||||
|
the Free Software Foundation, either version 3 of the License, or
|
||||||
|
(at your option) any later version.
|
||||||
|
|
||||||
|
This program is distributed in the hope that it will be useful,
|
||||||
|
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||||
|
GNU General Public License for more details.
|
||||||
|
|
||||||
|
You should have received a copy of the GNU General Public License
|
||||||
|
along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||||
|
|
||||||
|
Also add information on how to contact you by electronic and paper mail.
|
||||||
|
|
||||||
|
If the program does terminal interaction, make it output a short
|
||||||
|
notice like this when it starts in an interactive mode:
|
||||||
|
|
||||||
|
<program> Copyright (C) <year> <name of author>
|
||||||
|
This program comes with ABSOLUTELY NO WARRANTY; for details type `show w'.
|
||||||
|
This is free software, and you are welcome to redistribute it
|
||||||
|
under certain conditions; type `show c' for details.
|
||||||
|
|
||||||
|
The hypothetical commands `show w' and `show c' should show the appropriate
|
||||||
|
parts of the General Public License. Of course, your program's commands
|
||||||
|
might be different; for a GUI interface, you would use an "about box".
|
||||||
|
|
||||||
|
You should also get your employer (if you work as a programmer) or school,
|
||||||
|
if any, to sign a "copyright disclaimer" for the program, if necessary.
|
||||||
|
For more information on this, and how to apply and follow the GNU GPL, see
|
||||||
|
<https://www.gnu.org/licenses/>.
|
||||||
|
|
||||||
|
The GNU General Public License does not permit incorporating your program
|
||||||
|
into proprietary programs. If your program is a subroutine library, you
|
||||||
|
may consider it more useful to permit linking proprietary applications with
|
||||||
|
the library. If this is what you want to do, use the GNU Lesser General
|
||||||
|
Public License instead of this License. But first, please read
|
||||||
|
<https://www.gnu.org/licenses/why-not-lgpl.html>.
|
@ -1,8 +0,0 @@
|
|||||||
bromolow 3.10.108
|
|
||||||
apollolake 4.4.180
|
|
||||||
broadwell 4.4.180
|
|
||||||
broadwellnk 4.4.180
|
|
||||||
denverton 4.4.180
|
|
||||||
geminilake 4.4.180
|
|
||||||
v1000 4.4.180
|
|
||||||
purley 4.4.180
|
|
19
README.md
19
README.md
@ -1 +1,18 @@
|
|||||||
# arpl
|
# Arc Loader for DSM 7.x
|
||||||
|
|
||||||
|
### Bugreport
|
||||||
|
|
||||||
|
- Before you report an issue or bug please read the <a href="https://github.com/AuxXxilium/AuxXxilium/wiki">Wiki</a> and make sure your setting are correct.
|
||||||
|
- If you have to report an issue or bug:
|
||||||
|
- Hardware you use (Networkchip, Sata/HBA/Raid Controller)
|
||||||
|
- Screenshot the Systeminfo (Arc - Sysinfo) !!! <- IMPORTANT
|
||||||
|
|
||||||
|
### Links
|
||||||
|
|
||||||
|
- <a href="https://github.com/AuxXxilium">Overview</a>
|
||||||
|
- <a href="https://github.com/AuxXxilium/AuxXxilium/wiki">Wiki and Informations</a>
|
||||||
|
- <a href="https://github.com/AuxXxilium/arc/releases?q=prerelease%3Afalse&expanded=true">Download Release Build</a>
|
||||||
|
- <a href="https://github.com/AuxXxilium/arc/releases?q=prerelease%3Atrue&expanded=true">Download Development Build</a>
|
||||||
|
|
||||||
|
### Thanks
|
||||||
|
All code was based on the work of TTG, pocopico, jumkey, fbelavenuto, wjz304 and others involved in continuing TTG's original redpill-load project.
|
23
TODO
23
TODO
@ -1,23 +0,0 @@
|
|||||||
A fazer
|
|
||||||
- Implementar update do bzimage e ramdisk online
|
|
||||||
|
|
||||||
Concluidos:
|
|
||||||
- Generalizar código dos addons
|
|
||||||
- Implementar checagem de conflito entre addons *** Usado alternativa de ter listagem de módulos não necessários ***
|
|
||||||
- Tirar MAXDISKS dos arquivos dos modelos e adicionar menu no synoinfo para configurar máximo de HDs
|
|
||||||
- mudar na configs dos modelos os módulos builtin para módulos não necessários
|
|
||||||
- Mudar palavra "extra" para "cmdline"
|
|
||||||
- Implementar exibição de cmdline e synoinfo dos modelos
|
|
||||||
- Adicionar checagem no grub para exibir ou não menu de boot
|
|
||||||
- Bug no boot.sh, se usuário mudar a variável netif_num o script repassa os macX sem considerar o novo número de interfaces
|
|
||||||
*** TIRADO obrigatoriedade no LKM de passar esses parâmetros ***
|
|
||||||
- bug com macs, com 2 placas os MACs podem se inverter, checar isso
|
|
||||||
*** Resolvido com solução anterior ***
|
|
||||||
- Verifica se plataforma vai rodar na máquina checando as flags da CPU
|
|
||||||
- Implementar seleção da versão do LKM (dev ou prod)
|
|
||||||
- Usando TTYD para acesso via web
|
|
||||||
- Verificar se fica legal colocar na config dos modelos os addons obrigatórios como o qjs-dtb *** Usado outra maneira ***
|
|
||||||
- Implementar escolha de maxdisks
|
|
||||||
|
|
||||||
|
|
||||||
https://kb.synology.com/en-me/DSM/tutorial/What_kind_of_CPU_does_my_NAS_have
|
|
@ -1,22 +1,14 @@
|
|||||||
# https://taskfile.dev
|
# https://taskfile.dev
|
||||||
|
|
||||||
version: '3'
|
version: "3"
|
||||||
|
|
||||||
tasks:
|
tasks:
|
||||||
build-img:
|
build-img:
|
||||||
cmds:
|
cmds:
|
||||||
- ./img-gen.sh
|
- ./img-gen.sh
|
||||||
|
|
||||||
build-docker-img:
|
compile-kpatch:
|
||||||
dir: docker
|
dir: kpatch
|
||||||
cmds:
|
cmds:
|
||||||
- ./build.sh
|
- make clean all
|
||||||
|
- mv kpatch ../files/board/arc/overlayfs/opt/arc/
|
||||||
compile-lkms:
|
|
||||||
cmds:
|
|
||||||
- ./compile-lkm.sh
|
|
||||||
|
|
||||||
compile-addons:
|
|
||||||
dir: addons
|
|
||||||
cmds:
|
|
||||||
- ./compile-addons.sh {{.CLI_ARGS}}
|
|
@ -1,6 +0,0 @@
|
|||||||
if [ "${1}" = "rd" ]; then
|
|
||||||
echo "Installing module for Plan 9 Resource Sharing Support (9P2000)"
|
|
||||||
${INSMOD} "/modules/9pnet.ko"
|
|
||||||
${INSMOD} "/modules/9pnet_virtio.ko"
|
|
||||||
${INSMOD} "/modules/9p.ko" ${PARAMS}
|
|
||||||
fi
|
|
@ -1,28 +0,0 @@
|
|||||||
version: 1
|
|
||||||
name: 9p
|
|
||||||
description: "Driver for Plan 9 Resource Sharing Support (9P2000)"
|
|
||||||
available-for:
|
|
||||||
bromolow-3.10.108:
|
|
||||||
install-script: &script "install.sh"
|
|
||||||
modules: true
|
|
||||||
apollolake-4.4.180:
|
|
||||||
install-script: *script
|
|
||||||
modules: true
|
|
||||||
broadwell-4.4.180:
|
|
||||||
install-script: *script
|
|
||||||
modules: true
|
|
||||||
broadwellnk-4.4.180:
|
|
||||||
install-script: *script
|
|
||||||
modules: true
|
|
||||||
denverton-4.4.180:
|
|
||||||
install-script: *script
|
|
||||||
modules: true
|
|
||||||
geminilake-4.4.180:
|
|
||||||
install-script: *script
|
|
||||||
modules: true
|
|
||||||
v1000-4.4.180:
|
|
||||||
install-script: *script
|
|
||||||
modules: true
|
|
||||||
purley-4.4.180:
|
|
||||||
install-script: *script
|
|
||||||
modules: true
|
|
@ -1,36 +0,0 @@
|
|||||||
obj-m := 9p.o
|
|
||||||
|
|
||||||
9p-objs := \
|
|
||||||
vfs_super.o \
|
|
||||||
vfs_inode.o \
|
|
||||||
vfs_inode_dotl.o \
|
|
||||||
vfs_addr.o \
|
|
||||||
vfs_file.o \
|
|
||||||
vfs_dir.o \
|
|
||||||
vfs_dentry.o \
|
|
||||||
v9fs.o \
|
|
||||||
fid.o \
|
|
||||||
xattr.o \
|
|
||||||
xattr_user.o
|
|
||||||
|
|
||||||
9p-y += cache.o
|
|
||||||
9p-n += acl.o
|
|
||||||
|
|
||||||
obj-m := 9pnet.o
|
|
||||||
obj-m += 9pnet_virtio.o
|
|
||||||
obj-n += 9pnet_rdma.o
|
|
||||||
|
|
||||||
9pnet-objs := \
|
|
||||||
mod.o \
|
|
||||||
client.o \
|
|
||||||
error.o \
|
|
||||||
util.o \
|
|
||||||
protocol.o \
|
|
||||||
trans_fd.o \
|
|
||||||
trans_common.o \
|
|
||||||
|
|
||||||
9pnet_virtio-objs := \
|
|
||||||
trans_virtio.o
|
|
||||||
|
|
||||||
9pnet_rdma-objs := \
|
|
||||||
trans_rdma.o
|
|
@ -1,375 +0,0 @@
|
|||||||
/*
|
|
||||||
* Copyright IBM Corporation, 2010
|
|
||||||
* Author Aneesh Kumar K.V <aneesh.kumar@linux.vnet.ibm.com>
|
|
||||||
*
|
|
||||||
* This program is free software; you can redistribute it and/or modify it
|
|
||||||
* under the terms of version 2.1 of the GNU Lesser General Public License
|
|
||||||
* as published by the Free Software Foundation.
|
|
||||||
*
|
|
||||||
* This program is distributed in the hope that it would be useful, but
|
|
||||||
* WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
||||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
|
|
||||||
*
|
|
||||||
*/
|
|
||||||
|
|
||||||
#include <linux/module.h>
|
|
||||||
#include <linux/fs.h>
|
|
||||||
#include <net/9p/9p.h>
|
|
||||||
#include <net/9p/client.h>
|
|
||||||
#include <linux/slab.h>
|
|
||||||
#include <linux/sched.h>
|
|
||||||
#include <linux/posix_acl_xattr.h>
|
|
||||||
#include "xattr.h"
|
|
||||||
#include "acl.h"
|
|
||||||
#include "v9fs.h"
|
|
||||||
#include "v9fs_vfs.h"
|
|
||||||
#include "fid.h"
|
|
||||||
|
|
||||||
static struct posix_acl *__v9fs_get_acl(struct p9_fid *fid, char *name)
|
|
||||||
{
|
|
||||||
ssize_t size;
|
|
||||||
void *value = NULL;
|
|
||||||
struct posix_acl *acl = NULL;
|
|
||||||
|
|
||||||
size = v9fs_fid_xattr_get(fid, name, NULL, 0);
|
|
||||||
if (size > 0) {
|
|
||||||
value = kzalloc(size, GFP_NOFS);
|
|
||||||
if (!value)
|
|
||||||
return ERR_PTR(-ENOMEM);
|
|
||||||
size = v9fs_fid_xattr_get(fid, name, value, size);
|
|
||||||
if (size > 0) {
|
|
||||||
acl = posix_acl_from_xattr(&init_user_ns, value, size);
|
|
||||||
if (IS_ERR(acl))
|
|
||||||
goto err_out;
|
|
||||||
}
|
|
||||||
} else if (size == -ENODATA || size == 0 ||
|
|
||||||
size == -ENOSYS || size == -EOPNOTSUPP) {
|
|
||||||
acl = NULL;
|
|
||||||
} else
|
|
||||||
acl = ERR_PTR(-EIO);
|
|
||||||
|
|
||||||
err_out:
|
|
||||||
kfree(value);
|
|
||||||
return acl;
|
|
||||||
}
|
|
||||||
|
|
||||||
int v9fs_get_acl(struct inode *inode, struct p9_fid *fid)
|
|
||||||
{
|
|
||||||
int retval = 0;
|
|
||||||
struct posix_acl *pacl, *dacl;
|
|
||||||
struct v9fs_session_info *v9ses;
|
|
||||||
|
|
||||||
v9ses = v9fs_inode2v9ses(inode);
|
|
||||||
if (((v9ses->flags & V9FS_ACCESS_MASK) != V9FS_ACCESS_CLIENT) ||
|
|
||||||
((v9ses->flags & V9FS_ACL_MASK) != V9FS_POSIX_ACL)) {
|
|
||||||
set_cached_acl(inode, ACL_TYPE_DEFAULT, NULL);
|
|
||||||
set_cached_acl(inode, ACL_TYPE_ACCESS, NULL);
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
/* get the default/access acl values and cache them */
|
|
||||||
dacl = __v9fs_get_acl(fid, POSIX_ACL_XATTR_DEFAULT);
|
|
||||||
pacl = __v9fs_get_acl(fid, POSIX_ACL_XATTR_ACCESS);
|
|
||||||
|
|
||||||
if (!IS_ERR(dacl) && !IS_ERR(pacl)) {
|
|
||||||
set_cached_acl(inode, ACL_TYPE_DEFAULT, dacl);
|
|
||||||
set_cached_acl(inode, ACL_TYPE_ACCESS, pacl);
|
|
||||||
} else
|
|
||||||
retval = -EIO;
|
|
||||||
|
|
||||||
if (!IS_ERR(dacl))
|
|
||||||
posix_acl_release(dacl);
|
|
||||||
|
|
||||||
if (!IS_ERR(pacl))
|
|
||||||
posix_acl_release(pacl);
|
|
||||||
|
|
||||||
return retval;
|
|
||||||
}
|
|
||||||
|
|
||||||
static struct posix_acl *v9fs_get_cached_acl(struct inode *inode, int type)
|
|
||||||
{
|
|
||||||
struct posix_acl *acl;
|
|
||||||
/*
|
|
||||||
* 9p Always cache the acl value when
|
|
||||||
* instantiating the inode (v9fs_inode_from_fid)
|
|
||||||
*/
|
|
||||||
acl = get_cached_acl(inode, type);
|
|
||||||
BUG_ON(acl == ACL_NOT_CACHED);
|
|
||||||
return acl;
|
|
||||||
}
|
|
||||||
|
|
||||||
struct posix_acl *v9fs_iop_get_acl(struct inode *inode, int type)
|
|
||||||
{
|
|
||||||
struct v9fs_session_info *v9ses;
|
|
||||||
|
|
||||||
v9ses = v9fs_inode2v9ses(inode);
|
|
||||||
if (((v9ses->flags & V9FS_ACCESS_MASK) != V9FS_ACCESS_CLIENT) ||
|
|
||||||
((v9ses->flags & V9FS_ACL_MASK) != V9FS_POSIX_ACL)) {
|
|
||||||
/*
|
|
||||||
* On access = client and acl = on mode get the acl
|
|
||||||
* values from the server
|
|
||||||
*/
|
|
||||||
return NULL;
|
|
||||||
}
|
|
||||||
return v9fs_get_cached_acl(inode, type);
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
static int v9fs_set_acl(struct p9_fid *fid, int type, struct posix_acl *acl)
|
|
||||||
{
|
|
||||||
int retval;
|
|
||||||
char *name;
|
|
||||||
size_t size;
|
|
||||||
void *buffer;
|
|
||||||
if (!acl)
|
|
||||||
return 0;
|
|
||||||
|
|
||||||
/* Set a setxattr request to server */
|
|
||||||
size = posix_acl_xattr_size(acl->a_count);
|
|
||||||
buffer = kmalloc(size, GFP_KERNEL);
|
|
||||||
if (!buffer)
|
|
||||||
return -ENOMEM;
|
|
||||||
retval = posix_acl_to_xattr(&init_user_ns, acl, buffer, size);
|
|
||||||
if (retval < 0)
|
|
||||||
goto err_free_out;
|
|
||||||
switch (type) {
|
|
||||||
case ACL_TYPE_ACCESS:
|
|
||||||
name = POSIX_ACL_XATTR_ACCESS;
|
|
||||||
break;
|
|
||||||
case ACL_TYPE_DEFAULT:
|
|
||||||
name = POSIX_ACL_XATTR_DEFAULT;
|
|
||||||
break;
|
|
||||||
default:
|
|
||||||
BUG();
|
|
||||||
}
|
|
||||||
retval = v9fs_fid_xattr_set(fid, name, buffer, size, 0);
|
|
||||||
err_free_out:
|
|
||||||
kfree(buffer);
|
|
||||||
return retval;
|
|
||||||
}
|
|
||||||
|
|
||||||
int v9fs_acl_chmod(struct inode *inode, struct p9_fid *fid)
|
|
||||||
{
|
|
||||||
int retval = 0;
|
|
||||||
struct posix_acl *acl;
|
|
||||||
|
|
||||||
if (S_ISLNK(inode->i_mode))
|
|
||||||
return -EOPNOTSUPP;
|
|
||||||
acl = v9fs_get_cached_acl(inode, ACL_TYPE_ACCESS);
|
|
||||||
if (acl) {
|
|
||||||
retval = posix_acl_chmod(&acl, GFP_KERNEL, inode->i_mode);
|
|
||||||
if (retval)
|
|
||||||
return retval;
|
|
||||||
set_cached_acl(inode, ACL_TYPE_ACCESS, acl);
|
|
||||||
retval = v9fs_set_acl(fid, ACL_TYPE_ACCESS, acl);
|
|
||||||
posix_acl_release(acl);
|
|
||||||
}
|
|
||||||
return retval;
|
|
||||||
}
|
|
||||||
|
|
||||||
int v9fs_set_create_acl(struct inode *inode, struct p9_fid *fid,
|
|
||||||
struct posix_acl *dacl, struct posix_acl *acl)
|
|
||||||
{
|
|
||||||
set_cached_acl(inode, ACL_TYPE_DEFAULT, dacl);
|
|
||||||
set_cached_acl(inode, ACL_TYPE_ACCESS, acl);
|
|
||||||
v9fs_set_acl(fid, ACL_TYPE_DEFAULT, dacl);
|
|
||||||
v9fs_set_acl(fid, ACL_TYPE_ACCESS, acl);
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
void v9fs_put_acl(struct posix_acl *dacl,
|
|
||||||
struct posix_acl *acl)
|
|
||||||
{
|
|
||||||
posix_acl_release(dacl);
|
|
||||||
posix_acl_release(acl);
|
|
||||||
}
|
|
||||||
|
|
||||||
int v9fs_acl_mode(struct inode *dir, umode_t *modep,
|
|
||||||
struct posix_acl **dpacl, struct posix_acl **pacl)
|
|
||||||
{
|
|
||||||
int retval = 0;
|
|
||||||
umode_t mode = *modep;
|
|
||||||
struct posix_acl *acl = NULL;
|
|
||||||
|
|
||||||
if (!S_ISLNK(mode)) {
|
|
||||||
acl = v9fs_get_cached_acl(dir, ACL_TYPE_DEFAULT);
|
|
||||||
if (IS_ERR(acl))
|
|
||||||
return PTR_ERR(acl);
|
|
||||||
if (!acl)
|
|
||||||
mode &= ~current_umask();
|
|
||||||
}
|
|
||||||
if (acl) {
|
|
||||||
if (S_ISDIR(mode))
|
|
||||||
*dpacl = posix_acl_dup(acl);
|
|
||||||
retval = posix_acl_create(&acl, GFP_NOFS, &mode);
|
|
||||||
if (retval < 0)
|
|
||||||
return retval;
|
|
||||||
if (retval > 0)
|
|
||||||
*pacl = acl;
|
|
||||||
else
|
|
||||||
posix_acl_release(acl);
|
|
||||||
}
|
|
||||||
*modep = mode;
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
static int v9fs_remote_get_acl(struct dentry *dentry, const char *name,
|
|
||||||
void *buffer, size_t size, int type)
|
|
||||||
{
|
|
||||||
char *full_name;
|
|
||||||
|
|
||||||
switch (type) {
|
|
||||||
case ACL_TYPE_ACCESS:
|
|
||||||
full_name = POSIX_ACL_XATTR_ACCESS;
|
|
||||||
break;
|
|
||||||
case ACL_TYPE_DEFAULT:
|
|
||||||
full_name = POSIX_ACL_XATTR_DEFAULT;
|
|
||||||
break;
|
|
||||||
default:
|
|
||||||
BUG();
|
|
||||||
}
|
|
||||||
return v9fs_xattr_get(dentry, full_name, buffer, size);
|
|
||||||
}
|
|
||||||
|
|
||||||
static int v9fs_xattr_get_acl(struct dentry *dentry, const char *name,
|
|
||||||
void *buffer, size_t size, int type)
|
|
||||||
{
|
|
||||||
struct v9fs_session_info *v9ses;
|
|
||||||
struct posix_acl *acl;
|
|
||||||
int error;
|
|
||||||
|
|
||||||
if (strcmp(name, "") != 0)
|
|
||||||
return -EINVAL;
|
|
||||||
|
|
||||||
v9ses = v9fs_dentry2v9ses(dentry);
|
|
||||||
/*
|
|
||||||
* We allow set/get/list of acl when access=client is not specified
|
|
||||||
*/
|
|
||||||
if ((v9ses->flags & V9FS_ACCESS_MASK) != V9FS_ACCESS_CLIENT)
|
|
||||||
return v9fs_remote_get_acl(dentry, name, buffer, size, type);
|
|
||||||
|
|
||||||
acl = v9fs_get_cached_acl(dentry->d_inode, type);
|
|
||||||
if (IS_ERR(acl))
|
|
||||||
return PTR_ERR(acl);
|
|
||||||
if (acl == NULL)
|
|
||||||
return -ENODATA;
|
|
||||||
error = posix_acl_to_xattr(&init_user_ns, acl, buffer, size);
|
|
||||||
posix_acl_release(acl);
|
|
||||||
|
|
||||||
return error;
|
|
||||||
}
|
|
||||||
|
|
||||||
static int v9fs_remote_set_acl(struct dentry *dentry, const char *name,
|
|
||||||
const void *value, size_t size,
|
|
||||||
int flags, int type)
|
|
||||||
{
|
|
||||||
char *full_name;
|
|
||||||
|
|
||||||
switch (type) {
|
|
||||||
case ACL_TYPE_ACCESS:
|
|
||||||
full_name = POSIX_ACL_XATTR_ACCESS;
|
|
||||||
break;
|
|
||||||
case ACL_TYPE_DEFAULT:
|
|
||||||
full_name = POSIX_ACL_XATTR_DEFAULT;
|
|
||||||
break;
|
|
||||||
default:
|
|
||||||
BUG();
|
|
||||||
}
|
|
||||||
return v9fs_xattr_set(dentry, full_name, value, size, flags);
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
static int v9fs_xattr_set_acl(struct dentry *dentry, const char *name,
|
|
||||||
const void *value, size_t size,
|
|
||||||
int flags, int type)
|
|
||||||
{
|
|
||||||
int retval;
|
|
||||||
struct posix_acl *acl;
|
|
||||||
struct v9fs_session_info *v9ses;
|
|
||||||
struct inode *inode = dentry->d_inode;
|
|
||||||
|
|
||||||
if (strcmp(name, "") != 0)
|
|
||||||
return -EINVAL;
|
|
||||||
|
|
||||||
v9ses = v9fs_dentry2v9ses(dentry);
|
|
||||||
/*
|
|
||||||
* set the attribute on the remote. Without even looking at the
|
|
||||||
* xattr value. We leave it to the server to validate
|
|
||||||
*/
|
|
||||||
if ((v9ses->flags & V9FS_ACCESS_MASK) != V9FS_ACCESS_CLIENT)
|
|
||||||
return v9fs_remote_set_acl(dentry, name,
|
|
||||||
value, size, flags, type);
|
|
||||||
|
|
||||||
if (S_ISLNK(inode->i_mode))
|
|
||||||
return -EOPNOTSUPP;
|
|
||||||
if (!inode_owner_or_capable(inode))
|
|
||||||
return -EPERM;
|
|
||||||
if (value) {
|
|
||||||
/* update the cached acl value */
|
|
||||||
acl = posix_acl_from_xattr(&init_user_ns, value, size);
|
|
||||||
if (IS_ERR(acl))
|
|
||||||
return PTR_ERR(acl);
|
|
||||||
else if (acl) {
|
|
||||||
retval = posix_acl_valid(acl);
|
|
||||||
if (retval)
|
|
||||||
goto err_out;
|
|
||||||
}
|
|
||||||
} else
|
|
||||||
acl = NULL;
|
|
||||||
|
|
||||||
switch (type) {
|
|
||||||
case ACL_TYPE_ACCESS:
|
|
||||||
name = POSIX_ACL_XATTR_ACCESS;
|
|
||||||
if (acl) {
|
|
||||||
struct iattr iattr;
|
|
||||||
|
|
||||||
retval = posix_acl_update_mode(inode, &iattr.ia_mode, &acl);
|
|
||||||
if (retval)
|
|
||||||
goto err_out;
|
|
||||||
if (!acl) {
|
|
||||||
/*
|
|
||||||
* ACL can be represented
|
|
||||||
* by the mode bits. So don't
|
|
||||||
* update ACL.
|
|
||||||
*/
|
|
||||||
value = NULL;
|
|
||||||
size = 0;
|
|
||||||
}
|
|
||||||
iattr.ia_valid = ATTR_MODE;
|
|
||||||
/* FIXME should we update ctime ?
|
|
||||||
* What is the following setxattr update the
|
|
||||||
* mode ?
|
|
||||||
*/
|
|
||||||
v9fs_vfs_setattr_dotl(dentry, &iattr);
|
|
||||||
}
|
|
||||||
break;
|
|
||||||
case ACL_TYPE_DEFAULT:
|
|
||||||
name = POSIX_ACL_XATTR_DEFAULT;
|
|
||||||
if (!S_ISDIR(inode->i_mode)) {
|
|
||||||
retval = acl ? -EINVAL : 0;
|
|
||||||
goto err_out;
|
|
||||||
}
|
|
||||||
break;
|
|
||||||
default:
|
|
||||||
BUG();
|
|
||||||
}
|
|
||||||
retval = v9fs_xattr_set(dentry, name, value, size, flags);
|
|
||||||
if (!retval)
|
|
||||||
set_cached_acl(inode, type, acl);
|
|
||||||
err_out:
|
|
||||||
posix_acl_release(acl);
|
|
||||||
return retval;
|
|
||||||
}
|
|
||||||
|
|
||||||
const struct xattr_handler v9fs_xattr_acl_access_handler = {
|
|
||||||
.prefix = POSIX_ACL_XATTR_ACCESS,
|
|
||||||
.flags = ACL_TYPE_ACCESS,
|
|
||||||
.get = v9fs_xattr_get_acl,
|
|
||||||
.set = v9fs_xattr_set_acl,
|
|
||||||
};
|
|
||||||
|
|
||||||
const struct xattr_handler v9fs_xattr_acl_default_handler = {
|
|
||||||
.prefix = POSIX_ACL_XATTR_DEFAULT,
|
|
||||||
.flags = ACL_TYPE_DEFAULT,
|
|
||||||
.get = v9fs_xattr_get_acl,
|
|
||||||
.set = v9fs_xattr_set_acl,
|
|
||||||
};
|
|
@ -1,55 +0,0 @@
|
|||||||
/*
|
|
||||||
* Copyright IBM Corporation, 2010
|
|
||||||
* Author Aneesh Kumar K.V <aneesh.kumar@linux.vnet.ibm.com>
|
|
||||||
*
|
|
||||||
* This program is free software; you can redistribute it and/or modify it
|
|
||||||
* under the terms of version 2.1 of the GNU Lesser General Public License
|
|
||||||
* as published by the Free Software Foundation.
|
|
||||||
*
|
|
||||||
* This program is distributed in the hope that it would be useful, but
|
|
||||||
* WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
||||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
|
|
||||||
*
|
|
||||||
*/
|
|
||||||
#ifndef FS_9P_ACL_H
|
|
||||||
#define FS_9P_ACL_H
|
|
||||||
|
|
||||||
#ifdef CONFIG_9P_FS_POSIX_ACL
|
|
||||||
extern int v9fs_get_acl(struct inode *, struct p9_fid *);
|
|
||||||
extern struct posix_acl *v9fs_iop_get_acl(struct inode *inode, int type);
|
|
||||||
extern int v9fs_acl_chmod(struct inode *, struct p9_fid *);
|
|
||||||
extern int v9fs_set_create_acl(struct inode *, struct p9_fid *,
|
|
||||||
struct posix_acl *, struct posix_acl *);
|
|
||||||
extern int v9fs_acl_mode(struct inode *dir, umode_t *modep,
|
|
||||||
struct posix_acl **dpacl, struct posix_acl **pacl);
|
|
||||||
extern void v9fs_put_acl(struct posix_acl *dacl, struct posix_acl *acl);
|
|
||||||
#else
|
|
||||||
#define v9fs_iop_get_acl NULL
|
|
||||||
static inline int v9fs_get_acl(struct inode *inode, struct p9_fid *fid)
|
|
||||||
{
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
static inline int v9fs_acl_chmod(struct inode *inode, struct p9_fid *fid)
|
|
||||||
{
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
static inline int v9fs_set_create_acl(struct inode *inode,
|
|
||||||
struct p9_fid *fid,
|
|
||||||
struct posix_acl *dacl,
|
|
||||||
struct posix_acl *acl)
|
|
||||||
{
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
static inline void v9fs_put_acl(struct posix_acl *dacl,
|
|
||||||
struct posix_acl *acl)
|
|
||||||
{
|
|
||||||
}
|
|
||||||
static inline int v9fs_acl_mode(struct inode *dir, umode_t *modep,
|
|
||||||
struct posix_acl **dpacl,
|
|
||||||
struct posix_acl **pacl)
|
|
||||||
{
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
#endif
|
|
||||||
#endif /* FS_9P_XATTR_H */
|
|
@ -1,415 +0,0 @@
|
|||||||
/*
|
|
||||||
* V9FS cache definitions.
|
|
||||||
*
|
|
||||||
* Copyright (C) 2009 by Abhishek Kulkarni <adkulkar@umail.iu.edu>
|
|
||||||
*
|
|
||||||
* This program is free software; you can redistribute it and/or modify
|
|
||||||
* it under the terms of the GNU General Public License version 2
|
|
||||||
* as published by the Free Software Foundation.
|
|
||||||
*
|
|
||||||
* This program is distributed in the hope that it will be useful,
|
|
||||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
||||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
||||||
* GNU General Public License for more details.
|
|
||||||
*
|
|
||||||
* You should have received a copy of the GNU General Public License
|
|
||||||
* along with this program; if not, write to:
|
|
||||||
* Free Software Foundation
|
|
||||||
* 51 Franklin Street, Fifth Floor
|
|
||||||
* Boston, MA 02111-1301 USA
|
|
||||||
*
|
|
||||||
*/
|
|
||||||
|
|
||||||
#include <linux/jiffies.h>
|
|
||||||
#include <linux/file.h>
|
|
||||||
#include <linux/slab.h>
|
|
||||||
#include <linux/stat.h>
|
|
||||||
#include <linux/sched.h>
|
|
||||||
#include <linux/fs.h>
|
|
||||||
#include <net/9p/9p.h>
|
|
||||||
|
|
||||||
#include "v9fs.h"
|
|
||||||
#include "cache.h"
|
|
||||||
|
|
||||||
#define CACHETAG_LEN 11
|
|
||||||
|
|
||||||
struct fscache_netfs v9fs_cache_netfs = {
|
|
||||||
.name = "9p",
|
|
||||||
.version = 0,
|
|
||||||
};
|
|
||||||
|
|
||||||
/**
|
|
||||||
* v9fs_random_cachetag - Generate a random tag to be associated
|
|
||||||
* with a new cache session.
|
|
||||||
*
|
|
||||||
* The value of jiffies is used for a fairly randomly cache tag.
|
|
||||||
*/
|
|
||||||
|
|
||||||
static
|
|
||||||
int v9fs_random_cachetag(struct v9fs_session_info *v9ses)
|
|
||||||
{
|
|
||||||
v9ses->cachetag = kmalloc(CACHETAG_LEN, GFP_KERNEL);
|
|
||||||
if (!v9ses->cachetag)
|
|
||||||
return -ENOMEM;
|
|
||||||
|
|
||||||
return scnprintf(v9ses->cachetag, CACHETAG_LEN, "%lu", jiffies);
|
|
||||||
}
|
|
||||||
|
|
||||||
static uint16_t v9fs_cache_session_get_key(const void *cookie_netfs_data,
|
|
||||||
void *buffer, uint16_t bufmax)
|
|
||||||
{
|
|
||||||
struct v9fs_session_info *v9ses;
|
|
||||||
uint16_t klen = 0;
|
|
||||||
|
|
||||||
v9ses = (struct v9fs_session_info *)cookie_netfs_data;
|
|
||||||
p9_debug(P9_DEBUG_FSC, "session %p buf %p size %u\n",
|
|
||||||
v9ses, buffer, bufmax);
|
|
||||||
|
|
||||||
if (v9ses->cachetag)
|
|
||||||
klen = strlen(v9ses->cachetag);
|
|
||||||
|
|
||||||
if (klen > bufmax)
|
|
||||||
return 0;
|
|
||||||
|
|
||||||
memcpy(buffer, v9ses->cachetag, klen);
|
|
||||||
p9_debug(P9_DEBUG_FSC, "cache session tag %s\n", v9ses->cachetag);
|
|
||||||
return klen;
|
|
||||||
}
|
|
||||||
|
|
||||||
const struct fscache_cookie_def v9fs_cache_session_index_def = {
|
|
||||||
.name = "9P.session",
|
|
||||||
.type = FSCACHE_COOKIE_TYPE_INDEX,
|
|
||||||
.get_key = v9fs_cache_session_get_key,
|
|
||||||
};
|
|
||||||
|
|
||||||
void v9fs_cache_session_get_cookie(struct v9fs_session_info *v9ses)
|
|
||||||
{
|
|
||||||
/* If no cache session tag was specified, we generate a random one. */
|
|
||||||
if (!v9ses->cachetag)
|
|
||||||
v9fs_random_cachetag(v9ses);
|
|
||||||
|
|
||||||
v9ses->fscache = fscache_acquire_cookie(v9fs_cache_netfs.primary_index,
|
|
||||||
&v9fs_cache_session_index_def,
|
|
||||||
v9ses);
|
|
||||||
p9_debug(P9_DEBUG_FSC, "session %p get cookie %p\n",
|
|
||||||
v9ses, v9ses->fscache);
|
|
||||||
}
|
|
||||||
|
|
||||||
void v9fs_cache_session_put_cookie(struct v9fs_session_info *v9ses)
|
|
||||||
{
|
|
||||||
p9_debug(P9_DEBUG_FSC, "session %p put cookie %p\n",
|
|
||||||
v9ses, v9ses->fscache);
|
|
||||||
fscache_relinquish_cookie(v9ses->fscache, 0);
|
|
||||||
v9ses->fscache = NULL;
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
static uint16_t v9fs_cache_inode_get_key(const void *cookie_netfs_data,
|
|
||||||
void *buffer, uint16_t bufmax)
|
|
||||||
{
|
|
||||||
const struct v9fs_inode *v9inode = cookie_netfs_data;
|
|
||||||
memcpy(buffer, &v9inode->qid.path, sizeof(v9inode->qid.path));
|
|
||||||
p9_debug(P9_DEBUG_FSC, "inode %p get key %llu\n",
|
|
||||||
&v9inode->vfs_inode, v9inode->qid.path);
|
|
||||||
return sizeof(v9inode->qid.path);
|
|
||||||
}
|
|
||||||
|
|
||||||
static void v9fs_cache_inode_get_attr(const void *cookie_netfs_data,
|
|
||||||
uint64_t *size)
|
|
||||||
{
|
|
||||||
const struct v9fs_inode *v9inode = cookie_netfs_data;
|
|
||||||
*size = i_size_read(&v9inode->vfs_inode);
|
|
||||||
|
|
||||||
p9_debug(P9_DEBUG_FSC, "inode %p get attr %llu\n",
|
|
||||||
&v9inode->vfs_inode, *size);
|
|
||||||
}
|
|
||||||
|
|
||||||
static uint16_t v9fs_cache_inode_get_aux(const void *cookie_netfs_data,
|
|
||||||
void *buffer, uint16_t buflen)
|
|
||||||
{
|
|
||||||
const struct v9fs_inode *v9inode = cookie_netfs_data;
|
|
||||||
memcpy(buffer, &v9inode->qid.version, sizeof(v9inode->qid.version));
|
|
||||||
p9_debug(P9_DEBUG_FSC, "inode %p get aux %u\n",
|
|
||||||
&v9inode->vfs_inode, v9inode->qid.version);
|
|
||||||
return sizeof(v9inode->qid.version);
|
|
||||||
}
|
|
||||||
|
|
||||||
static enum
|
|
||||||
fscache_checkaux v9fs_cache_inode_check_aux(void *cookie_netfs_data,
|
|
||||||
const void *buffer,
|
|
||||||
uint16_t buflen)
|
|
||||||
{
|
|
||||||
const struct v9fs_inode *v9inode = cookie_netfs_data;
|
|
||||||
|
|
||||||
if (buflen != sizeof(v9inode->qid.version))
|
|
||||||
return FSCACHE_CHECKAUX_OBSOLETE;
|
|
||||||
|
|
||||||
if (memcmp(buffer, &v9inode->qid.version,
|
|
||||||
sizeof(v9inode->qid.version)))
|
|
||||||
return FSCACHE_CHECKAUX_OBSOLETE;
|
|
||||||
|
|
||||||
return FSCACHE_CHECKAUX_OKAY;
|
|
||||||
}
|
|
||||||
|
|
||||||
static void v9fs_cache_inode_now_uncached(void *cookie_netfs_data)
|
|
||||||
{
|
|
||||||
struct v9fs_inode *v9inode = cookie_netfs_data;
|
|
||||||
struct pagevec pvec;
|
|
||||||
pgoff_t first;
|
|
||||||
int loop, nr_pages;
|
|
||||||
|
|
||||||
pagevec_init(&pvec, 0);
|
|
||||||
first = 0;
|
|
||||||
|
|
||||||
for (;;) {
|
|
||||||
nr_pages = pagevec_lookup(&pvec, v9inode->vfs_inode.i_mapping,
|
|
||||||
first,
|
|
||||||
PAGEVEC_SIZE - pagevec_count(&pvec));
|
|
||||||
if (!nr_pages)
|
|
||||||
break;
|
|
||||||
|
|
||||||
for (loop = 0; loop < nr_pages; loop++)
|
|
||||||
ClearPageFsCache(pvec.pages[loop]);
|
|
||||||
|
|
||||||
first = pvec.pages[nr_pages - 1]->index + 1;
|
|
||||||
|
|
||||||
pvec.nr = nr_pages;
|
|
||||||
pagevec_release(&pvec);
|
|
||||||
cond_resched();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
const struct fscache_cookie_def v9fs_cache_inode_index_def = {
|
|
||||||
.name = "9p.inode",
|
|
||||||
.type = FSCACHE_COOKIE_TYPE_DATAFILE,
|
|
||||||
.get_key = v9fs_cache_inode_get_key,
|
|
||||||
.get_attr = v9fs_cache_inode_get_attr,
|
|
||||||
.get_aux = v9fs_cache_inode_get_aux,
|
|
||||||
.check_aux = v9fs_cache_inode_check_aux,
|
|
||||||
.now_uncached = v9fs_cache_inode_now_uncached,
|
|
||||||
};
|
|
||||||
|
|
||||||
void v9fs_cache_inode_get_cookie(struct inode *inode)
|
|
||||||
{
|
|
||||||
struct v9fs_inode *v9inode;
|
|
||||||
struct v9fs_session_info *v9ses;
|
|
||||||
|
|
||||||
if (!S_ISREG(inode->i_mode))
|
|
||||||
return;
|
|
||||||
|
|
||||||
v9inode = V9FS_I(inode);
|
|
||||||
if (v9inode->fscache)
|
|
||||||
return;
|
|
||||||
|
|
||||||
v9ses = v9fs_inode2v9ses(inode);
|
|
||||||
v9inode->fscache = fscache_acquire_cookie(v9ses->fscache,
|
|
||||||
&v9fs_cache_inode_index_def,
|
|
||||||
v9inode);
|
|
||||||
|
|
||||||
p9_debug(P9_DEBUG_FSC, "inode %p get cookie %p\n",
|
|
||||||
inode, v9inode->fscache);
|
|
||||||
}
|
|
||||||
|
|
||||||
void v9fs_cache_inode_put_cookie(struct inode *inode)
|
|
||||||
{
|
|
||||||
struct v9fs_inode *v9inode = V9FS_I(inode);
|
|
||||||
|
|
||||||
if (!v9inode->fscache)
|
|
||||||
return;
|
|
||||||
p9_debug(P9_DEBUG_FSC, "inode %p put cookie %p\n",
|
|
||||||
inode, v9inode->fscache);
|
|
||||||
|
|
||||||
fscache_relinquish_cookie(v9inode->fscache, 0);
|
|
||||||
v9inode->fscache = NULL;
|
|
||||||
}
|
|
||||||
|
|
||||||
void v9fs_cache_inode_flush_cookie(struct inode *inode)
|
|
||||||
{
|
|
||||||
struct v9fs_inode *v9inode = V9FS_I(inode);
|
|
||||||
|
|
||||||
if (!v9inode->fscache)
|
|
||||||
return;
|
|
||||||
p9_debug(P9_DEBUG_FSC, "inode %p flush cookie %p\n",
|
|
||||||
inode, v9inode->fscache);
|
|
||||||
|
|
||||||
fscache_relinquish_cookie(v9inode->fscache, 1);
|
|
||||||
v9inode->fscache = NULL;
|
|
||||||
}
|
|
||||||
|
|
||||||
void v9fs_cache_inode_set_cookie(struct inode *inode, struct file *filp)
|
|
||||||
{
|
|
||||||
struct v9fs_inode *v9inode = V9FS_I(inode);
|
|
||||||
struct p9_fid *fid;
|
|
||||||
|
|
||||||
if (!v9inode->fscache)
|
|
||||||
return;
|
|
||||||
|
|
||||||
spin_lock(&v9inode->fscache_lock);
|
|
||||||
fid = filp->private_data;
|
|
||||||
if ((filp->f_flags & O_ACCMODE) != O_RDONLY)
|
|
||||||
v9fs_cache_inode_flush_cookie(inode);
|
|
||||||
else
|
|
||||||
v9fs_cache_inode_get_cookie(inode);
|
|
||||||
|
|
||||||
spin_unlock(&v9inode->fscache_lock);
|
|
||||||
}
|
|
||||||
|
|
||||||
void v9fs_cache_inode_reset_cookie(struct inode *inode)
|
|
||||||
{
|
|
||||||
struct v9fs_inode *v9inode = V9FS_I(inode);
|
|
||||||
struct v9fs_session_info *v9ses;
|
|
||||||
struct fscache_cookie *old;
|
|
||||||
|
|
||||||
if (!v9inode->fscache)
|
|
||||||
return;
|
|
||||||
|
|
||||||
old = v9inode->fscache;
|
|
||||||
|
|
||||||
spin_lock(&v9inode->fscache_lock);
|
|
||||||
fscache_relinquish_cookie(v9inode->fscache, 1);
|
|
||||||
|
|
||||||
v9ses = v9fs_inode2v9ses(inode);
|
|
||||||
v9inode->fscache = fscache_acquire_cookie(v9ses->fscache,
|
|
||||||
&v9fs_cache_inode_index_def,
|
|
||||||
v9inode);
|
|
||||||
p9_debug(P9_DEBUG_FSC, "inode %p revalidating cookie old %p new %p\n",
|
|
||||||
inode, old, v9inode->fscache);
|
|
||||||
|
|
||||||
spin_unlock(&v9inode->fscache_lock);
|
|
||||||
}
|
|
||||||
|
|
||||||
int __v9fs_fscache_release_page(struct page *page, gfp_t gfp)
|
|
||||||
{
|
|
||||||
struct inode *inode = page->mapping->host;
|
|
||||||
struct v9fs_inode *v9inode = V9FS_I(inode);
|
|
||||||
|
|
||||||
BUG_ON(!v9inode->fscache);
|
|
||||||
|
|
||||||
return fscache_maybe_release_page(v9inode->fscache, page, gfp);
|
|
||||||
}
|
|
||||||
|
|
||||||
void __v9fs_fscache_invalidate_page(struct page *page)
|
|
||||||
{
|
|
||||||
struct inode *inode = page->mapping->host;
|
|
||||||
struct v9fs_inode *v9inode = V9FS_I(inode);
|
|
||||||
|
|
||||||
BUG_ON(!v9inode->fscache);
|
|
||||||
|
|
||||||
if (PageFsCache(page)) {
|
|
||||||
fscache_wait_on_page_write(v9inode->fscache, page);
|
|
||||||
BUG_ON(!PageLocked(page));
|
|
||||||
fscache_uncache_page(v9inode->fscache, page);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
static void v9fs_vfs_readpage_complete(struct page *page, void *data,
|
|
||||||
int error)
|
|
||||||
{
|
|
||||||
if (!error)
|
|
||||||
SetPageUptodate(page);
|
|
||||||
|
|
||||||
unlock_page(page);
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* __v9fs_readpage_from_fscache - read a page from cache
|
|
||||||
*
|
|
||||||
* Returns 0 if the pages are in cache and a BIO is submitted,
|
|
||||||
* 1 if the pages are not in cache and -error otherwise.
|
|
||||||
*/
|
|
||||||
|
|
||||||
int __v9fs_readpage_from_fscache(struct inode *inode, struct page *page)
|
|
||||||
{
|
|
||||||
int ret;
|
|
||||||
const struct v9fs_inode *v9inode = V9FS_I(inode);
|
|
||||||
|
|
||||||
p9_debug(P9_DEBUG_FSC, "inode %p page %p\n", inode, page);
|
|
||||||
if (!v9inode->fscache)
|
|
||||||
return -ENOBUFS;
|
|
||||||
|
|
||||||
ret = fscache_read_or_alloc_page(v9inode->fscache,
|
|
||||||
page,
|
|
||||||
v9fs_vfs_readpage_complete,
|
|
||||||
NULL,
|
|
||||||
GFP_KERNEL);
|
|
||||||
switch (ret) {
|
|
||||||
case -ENOBUFS:
|
|
||||||
case -ENODATA:
|
|
||||||
p9_debug(P9_DEBUG_FSC, "page/inode not in cache %d\n", ret);
|
|
||||||
return 1;
|
|
||||||
case 0:
|
|
||||||
p9_debug(P9_DEBUG_FSC, "BIO submitted\n");
|
|
||||||
return ret;
|
|
||||||
default:
|
|
||||||
p9_debug(P9_DEBUG_FSC, "ret %d\n", ret);
|
|
||||||
return ret;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* __v9fs_readpages_from_fscache - read multiple pages from cache
|
|
||||||
*
|
|
||||||
* Returns 0 if the pages are in cache and a BIO is submitted,
|
|
||||||
* 1 if the pages are not in cache and -error otherwise.
|
|
||||||
*/
|
|
||||||
|
|
||||||
int __v9fs_readpages_from_fscache(struct inode *inode,
|
|
||||||
struct address_space *mapping,
|
|
||||||
struct list_head *pages,
|
|
||||||
unsigned *nr_pages)
|
|
||||||
{
|
|
||||||
int ret;
|
|
||||||
const struct v9fs_inode *v9inode = V9FS_I(inode);
|
|
||||||
|
|
||||||
p9_debug(P9_DEBUG_FSC, "inode %p pages %u\n", inode, *nr_pages);
|
|
||||||
if (!v9inode->fscache)
|
|
||||||
return -ENOBUFS;
|
|
||||||
|
|
||||||
ret = fscache_read_or_alloc_pages(v9inode->fscache,
|
|
||||||
mapping, pages, nr_pages,
|
|
||||||
v9fs_vfs_readpage_complete,
|
|
||||||
NULL,
|
|
||||||
mapping_gfp_mask(mapping));
|
|
||||||
switch (ret) {
|
|
||||||
case -ENOBUFS:
|
|
||||||
case -ENODATA:
|
|
||||||
p9_debug(P9_DEBUG_FSC, "pages/inodes not in cache %d\n", ret);
|
|
||||||
return 1;
|
|
||||||
case 0:
|
|
||||||
BUG_ON(!list_empty(pages));
|
|
||||||
BUG_ON(*nr_pages != 0);
|
|
||||||
p9_debug(P9_DEBUG_FSC, "BIO submitted\n");
|
|
||||||
return ret;
|
|
||||||
default:
|
|
||||||
p9_debug(P9_DEBUG_FSC, "ret %d\n", ret);
|
|
||||||
return ret;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* __v9fs_readpage_to_fscache - write a page to the cache
|
|
||||||
*
|
|
||||||
*/
|
|
||||||
|
|
||||||
void __v9fs_readpage_to_fscache(struct inode *inode, struct page *page)
|
|
||||||
{
|
|
||||||
int ret;
|
|
||||||
const struct v9fs_inode *v9inode = V9FS_I(inode);
|
|
||||||
|
|
||||||
p9_debug(P9_DEBUG_FSC, "inode %p page %p\n", inode, page);
|
|
||||||
ret = fscache_write_page(v9inode->fscache, page, GFP_KERNEL);
|
|
||||||
p9_debug(P9_DEBUG_FSC, "ret = %d\n", ret);
|
|
||||||
if (ret != 0)
|
|
||||||
v9fs_uncache_page(inode, page);
|
|
||||||
}
|
|
||||||
|
|
||||||
/*
|
|
||||||
* wait for a page to complete writing to the cache
|
|
||||||
*/
|
|
||||||
void __v9fs_fscache_wait_on_page_write(struct inode *inode, struct page *page)
|
|
||||||
{
|
|
||||||
const struct v9fs_inode *v9inode = V9FS_I(inode);
|
|
||||||
p9_debug(P9_DEBUG_FSC, "inode %p page %p\n", inode, page);
|
|
||||||
if (PageFsCache(page))
|
|
||||||
fscache_wait_on_page_write(v9inode->fscache, page);
|
|
||||||
}
|
|
@ -1,139 +0,0 @@
|
|||||||
/*
|
|
||||||
* V9FS cache definitions.
|
|
||||||
*
|
|
||||||
* Copyright (C) 2009 by Abhishek Kulkarni <adkulkar@umail.iu.edu>
|
|
||||||
*
|
|
||||||
* This program is free software; you can redistribute it and/or modify
|
|
||||||
* it under the terms of the GNU General Public License version 2
|
|
||||||
* as published by the Free Software Foundation.
|
|
||||||
*
|
|
||||||
* This program is distributed in the hope that it will be useful,
|
|
||||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
||||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
||||||
* GNU General Public License for more details.
|
|
||||||
*
|
|
||||||
* You should have received a copy of the GNU General Public License
|
|
||||||
* along with this program; if not, write to:
|
|
||||||
* Free Software Foundation
|
|
||||||
* 51 Franklin Street, Fifth Floor
|
|
||||||
* Boston, MA 02111-1301 USA
|
|
||||||
*
|
|
||||||
*/
|
|
||||||
|
|
||||||
#ifndef _9P_CACHE_H
|
|
||||||
#ifdef CONFIG_9P_FSCACHE
|
|
||||||
#include <linux/fscache.h>
|
|
||||||
#include <linux/spinlock.h>
|
|
||||||
|
|
||||||
extern struct fscache_netfs v9fs_cache_netfs;
|
|
||||||
extern const struct fscache_cookie_def v9fs_cache_session_index_def;
|
|
||||||
extern const struct fscache_cookie_def v9fs_cache_inode_index_def;
|
|
||||||
|
|
||||||
extern void v9fs_cache_session_get_cookie(struct v9fs_session_info *v9ses);
|
|
||||||
extern void v9fs_cache_session_put_cookie(struct v9fs_session_info *v9ses);
|
|
||||||
|
|
||||||
extern void v9fs_cache_inode_get_cookie(struct inode *inode);
|
|
||||||
extern void v9fs_cache_inode_put_cookie(struct inode *inode);
|
|
||||||
extern void v9fs_cache_inode_flush_cookie(struct inode *inode);
|
|
||||||
extern void v9fs_cache_inode_set_cookie(struct inode *inode, struct file *filp);
|
|
||||||
extern void v9fs_cache_inode_reset_cookie(struct inode *inode);
|
|
||||||
|
|
||||||
extern int __v9fs_cache_register(void);
|
|
||||||
extern void __v9fs_cache_unregister(void);
|
|
||||||
|
|
||||||
extern int __v9fs_fscache_release_page(struct page *page, gfp_t gfp);
|
|
||||||
extern void __v9fs_fscache_invalidate_page(struct page *page);
|
|
||||||
extern int __v9fs_readpage_from_fscache(struct inode *inode,
|
|
||||||
struct page *page);
|
|
||||||
extern int __v9fs_readpages_from_fscache(struct inode *inode,
|
|
||||||
struct address_space *mapping,
|
|
||||||
struct list_head *pages,
|
|
||||||
unsigned *nr_pages);
|
|
||||||
extern void __v9fs_readpage_to_fscache(struct inode *inode, struct page *page);
|
|
||||||
extern void __v9fs_fscache_wait_on_page_write(struct inode *inode,
|
|
||||||
struct page *page);
|
|
||||||
|
|
||||||
static inline int v9fs_fscache_release_page(struct page *page,
|
|
||||||
gfp_t gfp)
|
|
||||||
{
|
|
||||||
return __v9fs_fscache_release_page(page, gfp);
|
|
||||||
}
|
|
||||||
|
|
||||||
static inline void v9fs_fscache_invalidate_page(struct page *page)
|
|
||||||
{
|
|
||||||
__v9fs_fscache_invalidate_page(page);
|
|
||||||
}
|
|
||||||
|
|
||||||
static inline int v9fs_readpage_from_fscache(struct inode *inode,
|
|
||||||
struct page *page)
|
|
||||||
{
|
|
||||||
return __v9fs_readpage_from_fscache(inode, page);
|
|
||||||
}
|
|
||||||
|
|
||||||
static inline int v9fs_readpages_from_fscache(struct inode *inode,
|
|
||||||
struct address_space *mapping,
|
|
||||||
struct list_head *pages,
|
|
||||||
unsigned *nr_pages)
|
|
||||||
{
|
|
||||||
return __v9fs_readpages_from_fscache(inode, mapping, pages,
|
|
||||||
nr_pages);
|
|
||||||
}
|
|
||||||
|
|
||||||
static inline void v9fs_readpage_to_fscache(struct inode *inode,
|
|
||||||
struct page *page)
|
|
||||||
{
|
|
||||||
if (PageFsCache(page))
|
|
||||||
__v9fs_readpage_to_fscache(inode, page);
|
|
||||||
}
|
|
||||||
|
|
||||||
static inline void v9fs_uncache_page(struct inode *inode, struct page *page)
|
|
||||||
{
|
|
||||||
struct v9fs_inode *v9inode = V9FS_I(inode);
|
|
||||||
fscache_uncache_page(v9inode->fscache, page);
|
|
||||||
BUG_ON(PageFsCache(page));
|
|
||||||
}
|
|
||||||
|
|
||||||
static inline void v9fs_fscache_wait_on_page_write(struct inode *inode,
|
|
||||||
struct page *page)
|
|
||||||
{
|
|
||||||
return __v9fs_fscache_wait_on_page_write(inode, page);
|
|
||||||
}
|
|
||||||
|
|
||||||
#else /* CONFIG_9P_FSCACHE */
|
|
||||||
|
|
||||||
static inline int v9fs_fscache_release_page(struct page *page,
|
|
||||||
gfp_t gfp) {
|
|
||||||
return 1;
|
|
||||||
}
|
|
||||||
|
|
||||||
static inline void v9fs_fscache_invalidate_page(struct page *page) {}
|
|
||||||
|
|
||||||
static inline int v9fs_readpage_from_fscache(struct inode *inode,
|
|
||||||
struct page *page)
|
|
||||||
{
|
|
||||||
return -ENOBUFS;
|
|
||||||
}
|
|
||||||
|
|
||||||
static inline int v9fs_readpages_from_fscache(struct inode *inode,
|
|
||||||
struct address_space *mapping,
|
|
||||||
struct list_head *pages,
|
|
||||||
unsigned *nr_pages)
|
|
||||||
{
|
|
||||||
return -ENOBUFS;
|
|
||||||
}
|
|
||||||
|
|
||||||
static inline void v9fs_readpage_to_fscache(struct inode *inode,
|
|
||||||
struct page *page)
|
|
||||||
{}
|
|
||||||
|
|
||||||
static inline void v9fs_uncache_page(struct inode *inode, struct page *page)
|
|
||||||
{}
|
|
||||||
|
|
||||||
static inline void v9fs_fscache_wait_on_page_write(struct inode *inode,
|
|
||||||
struct page *page)
|
|
||||||
{
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
#endif /* CONFIG_9P_FSCACHE */
|
|
||||||
#endif /* _9P_CACHE_H */
|
|
File diff suppressed because it is too large
Load Diff
@ -1,247 +0,0 @@
|
|||||||
/*
|
|
||||||
* linux/fs/9p/error.c
|
|
||||||
*
|
|
||||||
* Error string handling
|
|
||||||
*
|
|
||||||
* Plan 9 uses error strings, Unix uses error numbers. These functions
|
|
||||||
* try to help manage that and provide for dynamically adding error
|
|
||||||
* mappings.
|
|
||||||
*
|
|
||||||
* Copyright (C) 2004 by Eric Van Hensbergen <ericvh@gmail.com>
|
|
||||||
* Copyright (C) 2002 by Ron Minnich <rminnich@lanl.gov>
|
|
||||||
*
|
|
||||||
* This program is free software; you can redistribute it and/or modify
|
|
||||||
* it under the terms of the GNU General Public License version 2
|
|
||||||
* as published by the Free Software Foundation.
|
|
||||||
*
|
|
||||||
* This program is distributed in the hope that it will be useful,
|
|
||||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
||||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
||||||
* GNU General Public License for more details.
|
|
||||||
*
|
|
||||||
* You should have received a copy of the GNU General Public License
|
|
||||||
* along with this program; if not, write to:
|
|
||||||
* Free Software Foundation
|
|
||||||
* 51 Franklin Street, Fifth Floor
|
|
||||||
* Boston, MA 02111-1301 USA
|
|
||||||
*
|
|
||||||
*/
|
|
||||||
|
|
||||||
#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
|
|
||||||
|
|
||||||
#include <linux/module.h>
|
|
||||||
#include <linux/list.h>
|
|
||||||
#include <linux/jhash.h>
|
|
||||||
#include <linux/errno.h>
|
|
||||||
#include <net/9p/9p.h>
|
|
||||||
|
|
||||||
/**
|
|
||||||
* struct errormap - map string errors from Plan 9 to Linux numeric ids
|
|
||||||
* @name: string sent over 9P
|
|
||||||
* @val: numeric id most closely representing @name
|
|
||||||
* @namelen: length of string
|
|
||||||
* @list: hash-table list for string lookup
|
|
||||||
*/
|
|
||||||
struct errormap {
|
|
||||||
char *name;
|
|
||||||
int val;
|
|
||||||
|
|
||||||
int namelen;
|
|
||||||
struct hlist_node list;
|
|
||||||
};
|
|
||||||
|
|
||||||
#define ERRHASHSZ 32
|
|
||||||
static struct hlist_head hash_errmap[ERRHASHSZ];
|
|
||||||
|
|
||||||
/* FixMe - reduce to a reasonable size */
|
|
||||||
static struct errormap errmap[] = {
|
|
||||||
{"Operation not permitted", EPERM},
|
|
||||||
{"wstat prohibited", EPERM},
|
|
||||||
{"No such file or directory", ENOENT},
|
|
||||||
{"directory entry not found", ENOENT},
|
|
||||||
{"file not found", ENOENT},
|
|
||||||
{"Interrupted system call", EINTR},
|
|
||||||
{"Input/output error", EIO},
|
|
||||||
{"No such device or address", ENXIO},
|
|
||||||
{"Argument list too long", E2BIG},
|
|
||||||
{"Bad file descriptor", EBADF},
|
|
||||||
{"Resource temporarily unavailable", EAGAIN},
|
|
||||||
{"Cannot allocate memory", ENOMEM},
|
|
||||||
{"Permission denied", EACCES},
|
|
||||||
{"Bad address", EFAULT},
|
|
||||||
{"Block device required", ENOTBLK},
|
|
||||||
{"Device or resource busy", EBUSY},
|
|
||||||
{"File exists", EEXIST},
|
|
||||||
{"Invalid cross-device link", EXDEV},
|
|
||||||
{"No such device", ENODEV},
|
|
||||||
{"Not a directory", ENOTDIR},
|
|
||||||
{"Is a directory", EISDIR},
|
|
||||||
{"Invalid argument", EINVAL},
|
|
||||||
{"Too many open files in system", ENFILE},
|
|
||||||
{"Too many open files", EMFILE},
|
|
||||||
{"Text file busy", ETXTBSY},
|
|
||||||
{"File too large", EFBIG},
|
|
||||||
{"No space left on device", ENOSPC},
|
|
||||||
{"Illegal seek", ESPIPE},
|
|
||||||
{"Read-only file system", EROFS},
|
|
||||||
{"Too many links", EMLINK},
|
|
||||||
{"Broken pipe", EPIPE},
|
|
||||||
{"Numerical argument out of domain", EDOM},
|
|
||||||
{"Numerical result out of range", ERANGE},
|
|
||||||
{"Resource deadlock avoided", EDEADLK},
|
|
||||||
{"File name too long", ENAMETOOLONG},
|
|
||||||
{"No locks available", ENOLCK},
|
|
||||||
{"Function not implemented", ENOSYS},
|
|
||||||
{"Directory not empty", ENOTEMPTY},
|
|
||||||
{"Too many levels of symbolic links", ELOOP},
|
|
||||||
{"No message of desired type", ENOMSG},
|
|
||||||
{"Identifier removed", EIDRM},
|
|
||||||
{"No data available", ENODATA},
|
|
||||||
{"Machine is not on the network", ENONET},
|
|
||||||
{"Package not installed", ENOPKG},
|
|
||||||
{"Object is remote", EREMOTE},
|
|
||||||
{"Link has been severed", ENOLINK},
|
|
||||||
{"Communication error on send", ECOMM},
|
|
||||||
{"Protocol error", EPROTO},
|
|
||||||
{"Bad message", EBADMSG},
|
|
||||||
{"File descriptor in bad state", EBADFD},
|
|
||||||
{"Streams pipe error", ESTRPIPE},
|
|
||||||
{"Too many users", EUSERS},
|
|
||||||
{"Socket operation on non-socket", ENOTSOCK},
|
|
||||||
{"Message too long", EMSGSIZE},
|
|
||||||
{"Protocol not available", ENOPROTOOPT},
|
|
||||||
{"Protocol not supported", EPROTONOSUPPORT},
|
|
||||||
{"Socket type not supported", ESOCKTNOSUPPORT},
|
|
||||||
{"Operation not supported", EOPNOTSUPP},
|
|
||||||
{"Protocol family not supported", EPFNOSUPPORT},
|
|
||||||
{"Network is down", ENETDOWN},
|
|
||||||
{"Network is unreachable", ENETUNREACH},
|
|
||||||
{"Network dropped connection on reset", ENETRESET},
|
|
||||||
{"Software caused connection abort", ECONNABORTED},
|
|
||||||
{"Connection reset by peer", ECONNRESET},
|
|
||||||
{"No buffer space available", ENOBUFS},
|
|
||||||
{"Transport endpoint is already connected", EISCONN},
|
|
||||||
{"Transport endpoint is not connected", ENOTCONN},
|
|
||||||
{"Cannot send after transport endpoint shutdown", ESHUTDOWN},
|
|
||||||
{"Connection timed out", ETIMEDOUT},
|
|
||||||
{"Connection refused", ECONNREFUSED},
|
|
||||||
{"Host is down", EHOSTDOWN},
|
|
||||||
{"No route to host", EHOSTUNREACH},
|
|
||||||
{"Operation already in progress", EALREADY},
|
|
||||||
{"Operation now in progress", EINPROGRESS},
|
|
||||||
{"Is a named type file", EISNAM},
|
|
||||||
{"Remote I/O error", EREMOTEIO},
|
|
||||||
{"Disk quota exceeded", EDQUOT},
|
|
||||||
/* errors from fossil, vacfs, and u9fs */
|
|
||||||
{"fid unknown or out of range", EBADF},
|
|
||||||
{"permission denied", EACCES},
|
|
||||||
{"file does not exist", ENOENT},
|
|
||||||
{"authentication failed", ECONNREFUSED},
|
|
||||||
{"bad offset in directory read", ESPIPE},
|
|
||||||
{"bad use of fid", EBADF},
|
|
||||||
{"wstat can't convert between files and directories", EPERM},
|
|
||||||
{"directory is not empty", ENOTEMPTY},
|
|
||||||
{"file exists", EEXIST},
|
|
||||||
{"file already exists", EEXIST},
|
|
||||||
{"file or directory already exists", EEXIST},
|
|
||||||
{"fid already in use", EBADF},
|
|
||||||
{"file in use", ETXTBSY},
|
|
||||||
{"i/o error", EIO},
|
|
||||||
{"file already open for I/O", ETXTBSY},
|
|
||||||
{"illegal mode", EINVAL},
|
|
||||||
{"illegal name", ENAMETOOLONG},
|
|
||||||
{"not a directory", ENOTDIR},
|
|
||||||
{"not a member of proposed group", EPERM},
|
|
||||||
{"not owner", EACCES},
|
|
||||||
{"only owner can change group in wstat", EACCES},
|
|
||||||
{"read only file system", EROFS},
|
|
||||||
{"no access to special file", EPERM},
|
|
||||||
{"i/o count too large", EIO},
|
|
||||||
{"unknown group", EINVAL},
|
|
||||||
{"unknown user", EINVAL},
|
|
||||||
{"bogus wstat buffer", EPROTO},
|
|
||||||
{"exclusive use file already open", EAGAIN},
|
|
||||||
{"corrupted directory entry", EIO},
|
|
||||||
{"corrupted file entry", EIO},
|
|
||||||
{"corrupted block label", EIO},
|
|
||||||
{"corrupted meta data", EIO},
|
|
||||||
{"illegal offset", EINVAL},
|
|
||||||
{"illegal path element", ENOENT},
|
|
||||||
{"root of file system is corrupted", EIO},
|
|
||||||
{"corrupted super block", EIO},
|
|
||||||
{"protocol botch", EPROTO},
|
|
||||||
{"file system is full", ENOSPC},
|
|
||||||
{"file is in use", EAGAIN},
|
|
||||||
{"directory entry is not allocated", ENOENT},
|
|
||||||
{"file is read only", EROFS},
|
|
||||||
{"file has been removed", EIDRM},
|
|
||||||
{"only support truncation to zero length", EPERM},
|
|
||||||
{"cannot remove root", EPERM},
|
|
||||||
{"file too big", EFBIG},
|
|
||||||
{"venti i/o error", EIO},
|
|
||||||
/* these are not errors */
|
|
||||||
{"u9fs rhostsauth: no authentication required", 0},
|
|
||||||
{"u9fs authnone: no authentication required", 0},
|
|
||||||
{NULL, -1}
|
|
||||||
};
|
|
||||||
|
|
||||||
/**
|
|
||||||
* p9_error_init - preload mappings into hash list
|
|
||||||
*
|
|
||||||
*/
|
|
||||||
|
|
||||||
int p9_error_init(void)
|
|
||||||
{
|
|
||||||
struct errormap *c;
|
|
||||||
int bucket;
|
|
||||||
|
|
||||||
/* initialize hash table */
|
|
||||||
for (bucket = 0; bucket < ERRHASHSZ; bucket++)
|
|
||||||
INIT_HLIST_HEAD(&hash_errmap[bucket]);
|
|
||||||
|
|
||||||
/* load initial error map into hash table */
|
|
||||||
for (c = errmap; c->name != NULL; c++) {
|
|
||||||
c->namelen = strlen(c->name);
|
|
||||||
bucket = jhash(c->name, c->namelen, 0) % ERRHASHSZ;
|
|
||||||
INIT_HLIST_NODE(&c->list);
|
|
||||||
hlist_add_head(&c->list, &hash_errmap[bucket]);
|
|
||||||
}
|
|
||||||
|
|
||||||
return 1;
|
|
||||||
}
|
|
||||||
EXPORT_SYMBOL(p9_error_init);
|
|
||||||
|
|
||||||
/**
|
|
||||||
* errstr2errno - convert error string to error number
|
|
||||||
* @errstr: error string
|
|
||||||
* @len: length of error string
|
|
||||||
*
|
|
||||||
*/
|
|
||||||
|
|
||||||
int p9_errstr2errno(char *errstr, int len)
|
|
||||||
{
|
|
||||||
int errno;
|
|
||||||
struct errormap *c;
|
|
||||||
int bucket;
|
|
||||||
|
|
||||||
errno = 0;
|
|
||||||
c = NULL;
|
|
||||||
bucket = jhash(errstr, len, 0) % ERRHASHSZ;
|
|
||||||
hlist_for_each_entry(c, &hash_errmap[bucket], list) {
|
|
||||||
if (c->namelen == len && !memcmp(c->name, errstr, len)) {
|
|
||||||
errno = c->val;
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if (errno == 0) {
|
|
||||||
/* TODO: if error isn't found, add it dynamically */
|
|
||||||
errstr[len] = 0;
|
|
||||||
pr_err("%s: server reported unknown error %s\n",
|
|
||||||
__func__, errstr);
|
|
||||||
errno = ESERVERFAULT;
|
|
||||||
}
|
|
||||||
|
|
||||||
return -errno;
|
|
||||||
}
|
|
||||||
EXPORT_SYMBOL(p9_errstr2errno);
|
|
@ -1,306 +0,0 @@
|
|||||||
/*
|
|
||||||
* V9FS FID Management
|
|
||||||
*
|
|
||||||
* Copyright (C) 2007 by Latchesar Ionkov <lucho@ionkov.net>
|
|
||||||
* Copyright (C) 2005, 2006 by Eric Van Hensbergen <ericvh@gmail.com>
|
|
||||||
*
|
|
||||||
* This program is free software; you can redistribute it and/or modify
|
|
||||||
* it under the terms of the GNU General Public License version 2
|
|
||||||
* as published by the Free Software Foundation.
|
|
||||||
*
|
|
||||||
* This program is distributed in the hope that it will be useful,
|
|
||||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
||||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
||||||
* GNU General Public License for more details.
|
|
||||||
*
|
|
||||||
* You should have received a copy of the GNU General Public License
|
|
||||||
* along with this program; if not, write to:
|
|
||||||
* Free Software Foundation
|
|
||||||
* 51 Franklin Street, Fifth Floor
|
|
||||||
* Boston, MA 02111-1301 USA
|
|
||||||
*
|
|
||||||
*/
|
|
||||||
|
|
||||||
#include <linux/module.h>
|
|
||||||
#include <linux/errno.h>
|
|
||||||
#include <linux/fs.h>
|
|
||||||
#include <linux/slab.h>
|
|
||||||
#include <linux/sched.h>
|
|
||||||
#include <linux/idr.h>
|
|
||||||
#include <net/9p/9p.h>
|
|
||||||
#include <net/9p/client.h>
|
|
||||||
|
|
||||||
#include "v9fs.h"
|
|
||||||
#include "v9fs_vfs.h"
|
|
||||||
#include "fid.h"
|
|
||||||
|
|
||||||
/**
|
|
||||||
* v9fs_fid_add - add a fid to a dentry
|
|
||||||
* @dentry: dentry that the fid is being added to
|
|
||||||
* @fid: fid to add
|
|
||||||
*
|
|
||||||
*/
|
|
||||||
|
|
||||||
static inline void __add_fid(struct dentry *dentry, struct p9_fid *fid)
|
|
||||||
{
|
|
||||||
hlist_add_head(&fid->dlist, (struct hlist_head *)&dentry->d_fsdata);
|
|
||||||
}
|
|
||||||
|
|
||||||
void v9fs_fid_add(struct dentry *dentry, struct p9_fid *fid)
|
|
||||||
{
|
|
||||||
spin_lock(&dentry->d_lock);
|
|
||||||
__add_fid(dentry, fid);
|
|
||||||
spin_unlock(&dentry->d_lock);
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* v9fs_fid_find - retrieve a fid that belongs to the specified uid
|
|
||||||
* @dentry: dentry to look for fid in
|
|
||||||
* @uid: return fid that belongs to the specified user
|
|
||||||
* @any: if non-zero, return any fid associated with the dentry
|
|
||||||
*
|
|
||||||
*/
|
|
||||||
|
|
||||||
static struct p9_fid *v9fs_fid_find(struct dentry *dentry, kuid_t uid, int any)
|
|
||||||
{
|
|
||||||
struct p9_fid *fid, *ret;
|
|
||||||
|
|
||||||
p9_debug(P9_DEBUG_VFS, " dentry: %s (%p) uid %d any %d\n",
|
|
||||||
dentry->d_name.name, dentry, from_kuid(&init_user_ns, uid),
|
|
||||||
any);
|
|
||||||
ret = NULL;
|
|
||||||
/* we'll recheck under lock if there's anything to look in */
|
|
||||||
if (dentry->d_fsdata) {
|
|
||||||
struct hlist_head *h = (struct hlist_head *)&dentry->d_fsdata;
|
|
||||||
spin_lock(&dentry->d_lock);
|
|
||||||
hlist_for_each_entry(fid, h, dlist) {
|
|
||||||
if (any || uid_eq(fid->uid, uid)) {
|
|
||||||
ret = fid;
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
spin_unlock(&dentry->d_lock);
|
|
||||||
}
|
|
||||||
|
|
||||||
return ret;
|
|
||||||
}
|
|
||||||
|
|
||||||
/*
|
|
||||||
* We need to hold v9ses->rename_sem as long as we hold references
|
|
||||||
* to returned path array. Array element contain pointers to
|
|
||||||
* dentry names.
|
|
||||||
*/
|
|
||||||
static int build_path_from_dentry(struct v9fs_session_info *v9ses,
|
|
||||||
struct dentry *dentry, char ***names)
|
|
||||||
{
|
|
||||||
int n = 0, i;
|
|
||||||
char **wnames;
|
|
||||||
struct dentry *ds;
|
|
||||||
|
|
||||||
for (ds = dentry; !IS_ROOT(ds); ds = ds->d_parent)
|
|
||||||
n++;
|
|
||||||
|
|
||||||
wnames = kmalloc(sizeof(char *) * n, GFP_KERNEL);
|
|
||||||
if (!wnames)
|
|
||||||
goto err_out;
|
|
||||||
|
|
||||||
for (ds = dentry, i = (n-1); i >= 0; i--, ds = ds->d_parent)
|
|
||||||
wnames[i] = (char *)ds->d_name.name;
|
|
||||||
|
|
||||||
*names = wnames;
|
|
||||||
return n;
|
|
||||||
err_out:
|
|
||||||
return -ENOMEM;
|
|
||||||
}
|
|
||||||
|
|
||||||
static struct p9_fid *v9fs_fid_lookup_with_uid(struct dentry *dentry,
|
|
||||||
kuid_t uid, int any)
|
|
||||||
{
|
|
||||||
struct dentry *ds;
|
|
||||||
char **wnames, *uname;
|
|
||||||
int i, n, l, clone, access;
|
|
||||||
struct v9fs_session_info *v9ses;
|
|
||||||
struct p9_fid *fid, *old_fid = NULL;
|
|
||||||
|
|
||||||
v9ses = v9fs_dentry2v9ses(dentry);
|
|
||||||
access = v9ses->flags & V9FS_ACCESS_MASK;
|
|
||||||
fid = v9fs_fid_find(dentry, uid, any);
|
|
||||||
if (fid)
|
|
||||||
return fid;
|
|
||||||
/*
|
|
||||||
* we don't have a matching fid. To do a TWALK we need
|
|
||||||
* parent fid. We need to prevent rename when we want to
|
|
||||||
* look at the parent.
|
|
||||||
*/
|
|
||||||
down_read(&v9ses->rename_sem);
|
|
||||||
ds = dentry->d_parent;
|
|
||||||
fid = v9fs_fid_find(ds, uid, any);
|
|
||||||
if (fid) {
|
|
||||||
/* Found the parent fid do a lookup with that */
|
|
||||||
fid = p9_client_walk(fid, 1, (char **)&dentry->d_name.name, 1);
|
|
||||||
goto fid_out;
|
|
||||||
}
|
|
||||||
up_read(&v9ses->rename_sem);
|
|
||||||
|
|
||||||
/* start from the root and try to do a lookup */
|
|
||||||
fid = v9fs_fid_find(dentry->d_sb->s_root, uid, any);
|
|
||||||
if (!fid) {
|
|
||||||
/* the user is not attached to the fs yet */
|
|
||||||
if (access == V9FS_ACCESS_SINGLE)
|
|
||||||
return ERR_PTR(-EPERM);
|
|
||||||
|
|
||||||
if (v9fs_proto_dotu(v9ses) || v9fs_proto_dotl(v9ses))
|
|
||||||
uname = NULL;
|
|
||||||
else
|
|
||||||
uname = v9ses->uname;
|
|
||||||
|
|
||||||
fid = p9_client_attach(v9ses->clnt, NULL, uname, uid,
|
|
||||||
v9ses->aname);
|
|
||||||
if (IS_ERR(fid))
|
|
||||||
return fid;
|
|
||||||
|
|
||||||
v9fs_fid_add(dentry->d_sb->s_root, fid);
|
|
||||||
}
|
|
||||||
/* If we are root ourself just return that */
|
|
||||||
if (dentry->d_sb->s_root == dentry)
|
|
||||||
return fid;
|
|
||||||
/*
|
|
||||||
* Do a multipath walk with attached root.
|
|
||||||
* When walking parent we need to make sure we
|
|
||||||
* don't have a parallel rename happening
|
|
||||||
*/
|
|
||||||
down_read(&v9ses->rename_sem);
|
|
||||||
n = build_path_from_dentry(v9ses, dentry, &wnames);
|
|
||||||
if (n < 0) {
|
|
||||||
fid = ERR_PTR(n);
|
|
||||||
goto err_out;
|
|
||||||
}
|
|
||||||
clone = 1;
|
|
||||||
i = 0;
|
|
||||||
while (i < n) {
|
|
||||||
l = min(n - i, P9_MAXWELEM);
|
|
||||||
/*
|
|
||||||
* We need to hold rename lock when doing a multipath
|
|
||||||
* walk to ensure none of the patch component change
|
|
||||||
*/
|
|
||||||
fid = p9_client_walk(fid, l, &wnames[i], clone);
|
|
||||||
if (IS_ERR(fid)) {
|
|
||||||
if (old_fid) {
|
|
||||||
/*
|
|
||||||
* If we fail, clunk fid which are mapping
|
|
||||||
* to path component and not the last component
|
|
||||||
* of the path.
|
|
||||||
*/
|
|
||||||
p9_client_clunk(old_fid);
|
|
||||||
}
|
|
||||||
kfree(wnames);
|
|
||||||
goto err_out;
|
|
||||||
}
|
|
||||||
old_fid = fid;
|
|
||||||
i += l;
|
|
||||||
clone = 0;
|
|
||||||
}
|
|
||||||
kfree(wnames);
|
|
||||||
fid_out:
|
|
||||||
if (!IS_ERR(fid)) {
|
|
||||||
spin_lock(&dentry->d_lock);
|
|
||||||
if (d_unhashed(dentry)) {
|
|
||||||
spin_unlock(&dentry->d_lock);
|
|
||||||
p9_client_clunk(fid);
|
|
||||||
fid = ERR_PTR(-ENOENT);
|
|
||||||
} else {
|
|
||||||
__add_fid(dentry, fid);
|
|
||||||
spin_unlock(&dentry->d_lock);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
err_out:
|
|
||||||
up_read(&v9ses->rename_sem);
|
|
||||||
return fid;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* v9fs_fid_lookup - lookup for a fid, try to walk if not found
|
|
||||||
* @dentry: dentry to look for fid in
|
|
||||||
*
|
|
||||||
* Look for a fid in the specified dentry for the current user.
|
|
||||||
* If no fid is found, try to create one walking from a fid from the parent
|
|
||||||
* dentry (if it has one), or the root dentry. If the user haven't accessed
|
|
||||||
* the fs yet, attach now and walk from the root.
|
|
||||||
*/
|
|
||||||
|
|
||||||
struct p9_fid *v9fs_fid_lookup(struct dentry *dentry)
|
|
||||||
{
|
|
||||||
kuid_t uid;
|
|
||||||
int any, access;
|
|
||||||
struct v9fs_session_info *v9ses;
|
|
||||||
|
|
||||||
v9ses = v9fs_dentry2v9ses(dentry);
|
|
||||||
access = v9ses->flags & V9FS_ACCESS_MASK;
|
|
||||||
switch (access) {
|
|
||||||
case V9FS_ACCESS_SINGLE:
|
|
||||||
case V9FS_ACCESS_USER:
|
|
||||||
case V9FS_ACCESS_CLIENT:
|
|
||||||
uid = current_fsuid();
|
|
||||||
any = 0;
|
|
||||||
break;
|
|
||||||
|
|
||||||
case V9FS_ACCESS_ANY:
|
|
||||||
uid = v9ses->uid;
|
|
||||||
any = 1;
|
|
||||||
break;
|
|
||||||
|
|
||||||
default:
|
|
||||||
uid = INVALID_UID;
|
|
||||||
any = 0;
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
return v9fs_fid_lookup_with_uid(dentry, uid, any);
|
|
||||||
}
|
|
||||||
|
|
||||||
struct p9_fid *v9fs_fid_clone(struct dentry *dentry)
|
|
||||||
{
|
|
||||||
struct p9_fid *fid, *ret;
|
|
||||||
|
|
||||||
fid = v9fs_fid_lookup(dentry);
|
|
||||||
if (IS_ERR(fid))
|
|
||||||
return fid;
|
|
||||||
|
|
||||||
ret = p9_client_walk(fid, 0, NULL, 1);
|
|
||||||
return ret;
|
|
||||||
}
|
|
||||||
|
|
||||||
static struct p9_fid *v9fs_fid_clone_with_uid(struct dentry *dentry, kuid_t uid)
|
|
||||||
{
|
|
||||||
struct p9_fid *fid, *ret;
|
|
||||||
|
|
||||||
fid = v9fs_fid_lookup_with_uid(dentry, uid, 0);
|
|
||||||
if (IS_ERR(fid))
|
|
||||||
return fid;
|
|
||||||
|
|
||||||
ret = p9_client_walk(fid, 0, NULL, 1);
|
|
||||||
return ret;
|
|
||||||
}
|
|
||||||
|
|
||||||
struct p9_fid *v9fs_writeback_fid(struct dentry *dentry)
|
|
||||||
{
|
|
||||||
int err;
|
|
||||||
struct p9_fid *fid;
|
|
||||||
|
|
||||||
fid = v9fs_fid_clone_with_uid(dentry, GLOBAL_ROOT_UID);
|
|
||||||
if (IS_ERR(fid))
|
|
||||||
goto error_out;
|
|
||||||
/*
|
|
||||||
* writeback fid will only be used to write back the
|
|
||||||
* dirty pages. We always request for the open fid in read-write
|
|
||||||
* mode so that a partial page write which result in page
|
|
||||||
* read can work.
|
|
||||||
*/
|
|
||||||
err = p9_client_open(fid, O_RDWR);
|
|
||||||
if (err < 0) {
|
|
||||||
p9_client_clunk(fid);
|
|
||||||
fid = ERR_PTR(err);
|
|
||||||
goto error_out;
|
|
||||||
}
|
|
||||||
error_out:
|
|
||||||
return fid;
|
|
||||||
}
|
|
@ -1,30 +0,0 @@
|
|||||||
/*
|
|
||||||
* V9FS FID Management
|
|
||||||
*
|
|
||||||
* Copyright (C) 2005 by Eric Van Hensbergen <ericvh@gmail.com>
|
|
||||||
*
|
|
||||||
* This program is free software; you can redistribute it and/or modify
|
|
||||||
* it under the terms of the GNU General Public License version 2
|
|
||||||
* as published by the Free Software Foundation.
|
|
||||||
*
|
|
||||||
* This program is distributed in the hope that it will be useful,
|
|
||||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
||||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
||||||
* GNU General Public License for more details.
|
|
||||||
*
|
|
||||||
* You should have received a copy of the GNU General Public License
|
|
||||||
* along with this program; if not, write to:
|
|
||||||
* Free Software Foundation
|
|
||||||
* 51 Franklin Street, Fifth Floor
|
|
||||||
* Boston, MA 02111-1301 USA
|
|
||||||
*
|
|
||||||
*/
|
|
||||||
#ifndef FS_9P_FID_H
|
|
||||||
#define FS_9P_FID_H
|
|
||||||
#include <linux/list.h>
|
|
||||||
|
|
||||||
struct p9_fid *v9fs_fid_lookup(struct dentry *dentry);
|
|
||||||
struct p9_fid *v9fs_fid_clone(struct dentry *dentry);
|
|
||||||
void v9fs_fid_add(struct dentry *dentry, struct p9_fid *fid);
|
|
||||||
struct p9_fid *v9fs_writeback_fid(struct dentry *dentry);
|
|
||||||
#endif
|
|
@ -1,201 +0,0 @@
|
|||||||
/*
|
|
||||||
* net/9p/9p.c
|
|
||||||
*
|
|
||||||
* 9P entry point
|
|
||||||
*
|
|
||||||
* Copyright (C) 2007 by Latchesar Ionkov <lucho@ionkov.net>
|
|
||||||
* Copyright (C) 2004 by Eric Van Hensbergen <ericvh@gmail.com>
|
|
||||||
* Copyright (C) 2002 by Ron Minnich <rminnich@lanl.gov>
|
|
||||||
*
|
|
||||||
* This program is free software; you can redistribute it and/or modify
|
|
||||||
* it under the terms of the GNU General Public License version 2
|
|
||||||
* as published by the Free Software Foundation.
|
|
||||||
*
|
|
||||||
* This program is distributed in the hope that it will be useful,
|
|
||||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
||||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
||||||
* GNU General Public License for more details.
|
|
||||||
*
|
|
||||||
* You should have received a copy of the GNU General Public License
|
|
||||||
* along with this program; if not, write to:
|
|
||||||
* Free Software Foundation
|
|
||||||
* 51 Franklin Street, Fifth Floor
|
|
||||||
* Boston, MA 02111-1301 USA
|
|
||||||
*
|
|
||||||
*/
|
|
||||||
|
|
||||||
#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
|
|
||||||
|
|
||||||
#include <linux/module.h>
|
|
||||||
#include <linux/errno.h>
|
|
||||||
#include <linux/sched.h>
|
|
||||||
#include <linux/moduleparam.h>
|
|
||||||
#include <net/9p/9p.h>
|
|
||||||
#include <linux/fs.h>
|
|
||||||
#include <linux/parser.h>
|
|
||||||
#include <net/9p/client.h>
|
|
||||||
#include <net/9p/transport.h>
|
|
||||||
#include <linux/list.h>
|
|
||||||
#include <linux/spinlock.h>
|
|
||||||
|
|
||||||
#ifdef CONFIG_NET_9P_DEBUG
|
|
||||||
unsigned int p9_debug_level = 0; /* feature-rific global debug level */
|
|
||||||
EXPORT_SYMBOL(p9_debug_level);
|
|
||||||
module_param_named(debug, p9_debug_level, uint, 0);
|
|
||||||
MODULE_PARM_DESC(debug, "9P debugging level");
|
|
||||||
|
|
||||||
void _p9_debug(enum p9_debug_flags level, const char *func,
|
|
||||||
const char *fmt, ...)
|
|
||||||
{
|
|
||||||
struct va_format vaf;
|
|
||||||
va_list args;
|
|
||||||
|
|
||||||
if ((p9_debug_level & level) != level)
|
|
||||||
return;
|
|
||||||
|
|
||||||
va_start(args, fmt);
|
|
||||||
|
|
||||||
vaf.fmt = fmt;
|
|
||||||
vaf.va = &args;
|
|
||||||
|
|
||||||
if (level == P9_DEBUG_9P)
|
|
||||||
pr_notice("(%8.8d) %pV", task_pid_nr(current), &vaf);
|
|
||||||
else
|
|
||||||
pr_notice("-- %s (%d): %pV", func, task_pid_nr(current), &vaf);
|
|
||||||
|
|
||||||
va_end(args);
|
|
||||||
}
|
|
||||||
EXPORT_SYMBOL(_p9_debug);
|
|
||||||
#endif
|
|
||||||
|
|
||||||
/*
|
|
||||||
* Dynamic Transport Registration Routines
|
|
||||||
*
|
|
||||||
*/
|
|
||||||
|
|
||||||
static DEFINE_SPINLOCK(v9fs_trans_lock);
|
|
||||||
static LIST_HEAD(v9fs_trans_list);
|
|
||||||
|
|
||||||
/**
|
|
||||||
* v9fs_register_trans - register a new transport with 9p
|
|
||||||
* @m: structure describing the transport module and entry points
|
|
||||||
*
|
|
||||||
*/
|
|
||||||
void v9fs_register_trans(struct p9_trans_module *m)
|
|
||||||
{
|
|
||||||
spin_lock(&v9fs_trans_lock);
|
|
||||||
list_add_tail(&m->list, &v9fs_trans_list);
|
|
||||||
spin_unlock(&v9fs_trans_lock);
|
|
||||||
}
|
|
||||||
EXPORT_SYMBOL(v9fs_register_trans);
|
|
||||||
|
|
||||||
/**
|
|
||||||
* v9fs_unregister_trans - unregister a 9p transport
|
|
||||||
* @m: the transport to remove
|
|
||||||
*
|
|
||||||
*/
|
|
||||||
void v9fs_unregister_trans(struct p9_trans_module *m)
|
|
||||||
{
|
|
||||||
spin_lock(&v9fs_trans_lock);
|
|
||||||
list_del_init(&m->list);
|
|
||||||
spin_unlock(&v9fs_trans_lock);
|
|
||||||
}
|
|
||||||
EXPORT_SYMBOL(v9fs_unregister_trans);
|
|
||||||
|
|
||||||
/**
|
|
||||||
* v9fs_get_trans_by_name - get transport with the matching name
|
|
||||||
* @name: string identifying transport
|
|
||||||
*
|
|
||||||
*/
|
|
||||||
struct p9_trans_module *v9fs_get_trans_by_name(char *s)
|
|
||||||
{
|
|
||||||
struct p9_trans_module *t, *found = NULL;
|
|
||||||
|
|
||||||
spin_lock(&v9fs_trans_lock);
|
|
||||||
|
|
||||||
list_for_each_entry(t, &v9fs_trans_list, list)
|
|
||||||
if (strcmp(t->name, s) == 0 &&
|
|
||||||
try_module_get(t->owner)) {
|
|
||||||
found = t;
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
|
|
||||||
spin_unlock(&v9fs_trans_lock);
|
|
||||||
return found;
|
|
||||||
}
|
|
||||||
EXPORT_SYMBOL(v9fs_get_trans_by_name);
|
|
||||||
|
|
||||||
/**
|
|
||||||
* v9fs_get_default_trans - get the default transport
|
|
||||||
*
|
|
||||||
*/
|
|
||||||
|
|
||||||
struct p9_trans_module *v9fs_get_default_trans(void)
|
|
||||||
{
|
|
||||||
struct p9_trans_module *t, *found = NULL;
|
|
||||||
|
|
||||||
spin_lock(&v9fs_trans_lock);
|
|
||||||
|
|
||||||
list_for_each_entry(t, &v9fs_trans_list, list)
|
|
||||||
if (t->def && try_module_get(t->owner)) {
|
|
||||||
found = t;
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (!found)
|
|
||||||
list_for_each_entry(t, &v9fs_trans_list, list)
|
|
||||||
if (try_module_get(t->owner)) {
|
|
||||||
found = t;
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
|
|
||||||
spin_unlock(&v9fs_trans_lock);
|
|
||||||
return found;
|
|
||||||
}
|
|
||||||
EXPORT_SYMBOL(v9fs_get_default_trans);
|
|
||||||
|
|
||||||
/**
|
|
||||||
* v9fs_put_trans - put trans
|
|
||||||
* @m: transport to put
|
|
||||||
*
|
|
||||||
*/
|
|
||||||
void v9fs_put_trans(struct p9_trans_module *m)
|
|
||||||
{
|
|
||||||
if (m)
|
|
||||||
module_put(m->owner);
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* init_p9 - Initialize module
|
|
||||||
*
|
|
||||||
*/
|
|
||||||
static int __init init_p9(void)
|
|
||||||
{
|
|
||||||
int ret = 0;
|
|
||||||
|
|
||||||
p9_error_init();
|
|
||||||
pr_info("Installing 9P2000 support\n");
|
|
||||||
p9_trans_fd_init();
|
|
||||||
|
|
||||||
return ret;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* exit_p9 - shutdown module
|
|
||||||
*
|
|
||||||
*/
|
|
||||||
|
|
||||||
static void __exit exit_p9(void)
|
|
||||||
{
|
|
||||||
pr_info("Unloading 9P2000 support\n");
|
|
||||||
|
|
||||||
p9_trans_fd_exit();
|
|
||||||
}
|
|
||||||
|
|
||||||
module_init(init_p9)
|
|
||||||
module_exit(exit_p9)
|
|
||||||
|
|
||||||
MODULE_AUTHOR("Latchesar Ionkov <lucho@ionkov.net>");
|
|
||||||
MODULE_AUTHOR("Eric Van Hensbergen <ericvh@gmail.com>");
|
|
||||||
MODULE_AUTHOR("Ron Minnich <rminnich@lanl.gov>");
|
|
||||||
MODULE_LICENSE("GPL");
|
|
@ -1,636 +0,0 @@
|
|||||||
/*
|
|
||||||
* net/9p/protocol.c
|
|
||||||
*
|
|
||||||
* 9P Protocol Support Code
|
|
||||||
*
|
|
||||||
* Copyright (C) 2008 by Eric Van Hensbergen <ericvh@gmail.com>
|
|
||||||
*
|
|
||||||
* Base on code from Anthony Liguori <aliguori@us.ibm.com>
|
|
||||||
* Copyright (C) 2008 by IBM, Corp.
|
|
||||||
*
|
|
||||||
* This program is free software; you can redistribute it and/or modify
|
|
||||||
* it under the terms of the GNU General Public License version 2
|
|
||||||
* as published by the Free Software Foundation.
|
|
||||||
*
|
|
||||||
* This program is distributed in the hope that it will be useful,
|
|
||||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
||||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
||||||
* GNU General Public License for more details.
|
|
||||||
*
|
|
||||||
* You should have received a copy of the GNU General Public License
|
|
||||||
* along with this program; if not, write to:
|
|
||||||
* Free Software Foundation
|
|
||||||
* 51 Franklin Street, Fifth Floor
|
|
||||||
* Boston, MA 02111-1301 USA
|
|
||||||
*
|
|
||||||
*/
|
|
||||||
|
|
||||||
#include <linux/module.h>
|
|
||||||
#include <linux/errno.h>
|
|
||||||
#include <linux/kernel.h>
|
|
||||||
#include <linux/uaccess.h>
|
|
||||||
#include <linux/slab.h>
|
|
||||||
#include <linux/sched.h>
|
|
||||||
#include <linux/stddef.h>
|
|
||||||
#include <linux/types.h>
|
|
||||||
#include <net/9p/9p.h>
|
|
||||||
#include <net/9p/client.h>
|
|
||||||
#include "protocol.h"
|
|
||||||
|
|
||||||
#include <trace/events/9p.h>
|
|
||||||
|
|
||||||
static int
|
|
||||||
p9pdu_writef(struct p9_fcall *pdu, int proto_version, const char *fmt, ...);
|
|
||||||
|
|
||||||
void p9stat_free(struct p9_wstat *stbuf)
|
|
||||||
{
|
|
||||||
kfree(stbuf->name);
|
|
||||||
kfree(stbuf->uid);
|
|
||||||
kfree(stbuf->gid);
|
|
||||||
kfree(stbuf->muid);
|
|
||||||
kfree(stbuf->extension);
|
|
||||||
}
|
|
||||||
EXPORT_SYMBOL(p9stat_free);
|
|
||||||
|
|
||||||
size_t pdu_read(struct p9_fcall *pdu, void *data, size_t size)
|
|
||||||
{
|
|
||||||
size_t len = min(pdu->size - pdu->offset, size);
|
|
||||||
memcpy(data, &pdu->sdata[pdu->offset], len);
|
|
||||||
pdu->offset += len;
|
|
||||||
return size - len;
|
|
||||||
}
|
|
||||||
|
|
||||||
static size_t pdu_write(struct p9_fcall *pdu, const void *data, size_t size)
|
|
||||||
{
|
|
||||||
size_t len = min(pdu->capacity - pdu->size, size);
|
|
||||||
memcpy(&pdu->sdata[pdu->size], data, len);
|
|
||||||
pdu->size += len;
|
|
||||||
return size - len;
|
|
||||||
}
|
|
||||||
|
|
||||||
static size_t
|
|
||||||
pdu_write_u(struct p9_fcall *pdu, const char __user *udata, size_t size)
|
|
||||||
{
|
|
||||||
size_t len = min(pdu->capacity - pdu->size, size);
|
|
||||||
if (copy_from_user(&pdu->sdata[pdu->size], udata, len))
|
|
||||||
len = 0;
|
|
||||||
|
|
||||||
pdu->size += len;
|
|
||||||
return size - len;
|
|
||||||
}
|
|
||||||
|
|
||||||
/*
|
|
||||||
b - int8_t
|
|
||||||
w - int16_t
|
|
||||||
d - int32_t
|
|
||||||
q - int64_t
|
|
||||||
s - string
|
|
||||||
u - numeric uid
|
|
||||||
g - numeric gid
|
|
||||||
S - stat
|
|
||||||
Q - qid
|
|
||||||
D - data blob (int32_t size followed by void *, results are not freed)
|
|
||||||
T - array of strings (int16_t count, followed by strings)
|
|
||||||
R - array of qids (int16_t count, followed by qids)
|
|
||||||
A - stat for 9p2000.L (p9_stat_dotl)
|
|
||||||
? - if optional = 1, continue parsing
|
|
||||||
*/
|
|
||||||
|
|
||||||
static int
|
|
||||||
p9pdu_vreadf(struct p9_fcall *pdu, int proto_version, const char *fmt,
|
|
||||||
va_list ap)
|
|
||||||
{
|
|
||||||
const char *ptr;
|
|
||||||
int errcode = 0;
|
|
||||||
|
|
||||||
for (ptr = fmt; *ptr; ptr++) {
|
|
||||||
switch (*ptr) {
|
|
||||||
case 'b':{
|
|
||||||
int8_t *val = va_arg(ap, int8_t *);
|
|
||||||
if (pdu_read(pdu, val, sizeof(*val))) {
|
|
||||||
errcode = -EFAULT;
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
break;
|
|
||||||
case 'w':{
|
|
||||||
int16_t *val = va_arg(ap, int16_t *);
|
|
||||||
__le16 le_val;
|
|
||||||
if (pdu_read(pdu, &le_val, sizeof(le_val))) {
|
|
||||||
errcode = -EFAULT;
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
*val = le16_to_cpu(le_val);
|
|
||||||
}
|
|
||||||
break;
|
|
||||||
case 'd':{
|
|
||||||
int32_t *val = va_arg(ap, int32_t *);
|
|
||||||
__le32 le_val;
|
|
||||||
if (pdu_read(pdu, &le_val, sizeof(le_val))) {
|
|
||||||
errcode = -EFAULT;
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
*val = le32_to_cpu(le_val);
|
|
||||||
}
|
|
||||||
break;
|
|
||||||
case 'q':{
|
|
||||||
int64_t *val = va_arg(ap, int64_t *);
|
|
||||||
__le64 le_val;
|
|
||||||
if (pdu_read(pdu, &le_val, sizeof(le_val))) {
|
|
||||||
errcode = -EFAULT;
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
*val = le64_to_cpu(le_val);
|
|
||||||
}
|
|
||||||
break;
|
|
||||||
case 's':{
|
|
||||||
char **sptr = va_arg(ap, char **);
|
|
||||||
uint16_t len;
|
|
||||||
|
|
||||||
errcode = p9pdu_readf(pdu, proto_version,
|
|
||||||
"w", &len);
|
|
||||||
if (errcode)
|
|
||||||
break;
|
|
||||||
|
|
||||||
*sptr = kmalloc(len + 1, GFP_NOFS);
|
|
||||||
if (*sptr == NULL) {
|
|
||||||
errcode = -EFAULT;
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
if (pdu_read(pdu, *sptr, len)) {
|
|
||||||
errcode = -EFAULT;
|
|
||||||
kfree(*sptr);
|
|
||||||
*sptr = NULL;
|
|
||||||
} else
|
|
||||||
(*sptr)[len] = 0;
|
|
||||||
}
|
|
||||||
break;
|
|
||||||
case 'u': {
|
|
||||||
kuid_t *uid = va_arg(ap, kuid_t *);
|
|
||||||
__le32 le_val;
|
|
||||||
if (pdu_read(pdu, &le_val, sizeof(le_val))) {
|
|
||||||
errcode = -EFAULT;
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
*uid = make_kuid(&init_user_ns,
|
|
||||||
le32_to_cpu(le_val));
|
|
||||||
} break;
|
|
||||||
case 'g': {
|
|
||||||
kgid_t *gid = va_arg(ap, kgid_t *);
|
|
||||||
__le32 le_val;
|
|
||||||
if (pdu_read(pdu, &le_val, sizeof(le_val))) {
|
|
||||||
errcode = -EFAULT;
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
*gid = make_kgid(&init_user_ns,
|
|
||||||
le32_to_cpu(le_val));
|
|
||||||
} break;
|
|
||||||
case 'Q':{
|
|
||||||
struct p9_qid *qid =
|
|
||||||
va_arg(ap, struct p9_qid *);
|
|
||||||
|
|
||||||
errcode = p9pdu_readf(pdu, proto_version, "bdq",
|
|
||||||
&qid->type, &qid->version,
|
|
||||||
&qid->path);
|
|
||||||
}
|
|
||||||
break;
|
|
||||||
case 'S':{
|
|
||||||
struct p9_wstat *stbuf =
|
|
||||||
va_arg(ap, struct p9_wstat *);
|
|
||||||
|
|
||||||
memset(stbuf, 0, sizeof(struct p9_wstat));
|
|
||||||
stbuf->n_uid = stbuf->n_muid = INVALID_UID;
|
|
||||||
stbuf->n_gid = INVALID_GID;
|
|
||||||
|
|
||||||
errcode =
|
|
||||||
p9pdu_readf(pdu, proto_version,
|
|
||||||
"wwdQdddqssss?sugu",
|
|
||||||
&stbuf->size, &stbuf->type,
|
|
||||||
&stbuf->dev, &stbuf->qid,
|
|
||||||
&stbuf->mode, &stbuf->atime,
|
|
||||||
&stbuf->mtime, &stbuf->length,
|
|
||||||
&stbuf->name, &stbuf->uid,
|
|
||||||
&stbuf->gid, &stbuf->muid,
|
|
||||||
&stbuf->extension,
|
|
||||||
&stbuf->n_uid, &stbuf->n_gid,
|
|
||||||
&stbuf->n_muid);
|
|
||||||
if (errcode)
|
|
||||||
p9stat_free(stbuf);
|
|
||||||
}
|
|
||||||
break;
|
|
||||||
case 'D':{
|
|
||||||
uint32_t *count = va_arg(ap, uint32_t *);
|
|
||||||
void **data = va_arg(ap, void **);
|
|
||||||
|
|
||||||
errcode =
|
|
||||||
p9pdu_readf(pdu, proto_version, "d", count);
|
|
||||||
if (!errcode) {
|
|
||||||
*count =
|
|
||||||
min_t(uint32_t, *count,
|
|
||||||
pdu->size - pdu->offset);
|
|
||||||
*data = &pdu->sdata[pdu->offset];
|
|
||||||
}
|
|
||||||
}
|
|
||||||
break;
|
|
||||||
case 'T':{
|
|
||||||
uint16_t *nwname = va_arg(ap, uint16_t *);
|
|
||||||
char ***wnames = va_arg(ap, char ***);
|
|
||||||
|
|
||||||
errcode = p9pdu_readf(pdu, proto_version,
|
|
||||||
"w", nwname);
|
|
||||||
if (!errcode) {
|
|
||||||
*wnames =
|
|
||||||
kmalloc(sizeof(char *) * *nwname,
|
|
||||||
GFP_NOFS);
|
|
||||||
if (!*wnames)
|
|
||||||
errcode = -ENOMEM;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (!errcode) {
|
|
||||||
int i;
|
|
||||||
|
|
||||||
for (i = 0; i < *nwname; i++) {
|
|
||||||
errcode =
|
|
||||||
p9pdu_readf(pdu,
|
|
||||||
proto_version,
|
|
||||||
"s",
|
|
||||||
&(*wnames)[i]);
|
|
||||||
if (errcode)
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if (errcode) {
|
|
||||||
if (*wnames) {
|
|
||||||
int i;
|
|
||||||
|
|
||||||
for (i = 0; i < *nwname; i++)
|
|
||||||
kfree((*wnames)[i]);
|
|
||||||
}
|
|
||||||
kfree(*wnames);
|
|
||||||
*wnames = NULL;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
break;
|
|
||||||
case 'R':{
|
|
||||||
int16_t *nwqid = va_arg(ap, int16_t *);
|
|
||||||
struct p9_qid **wqids =
|
|
||||||
va_arg(ap, struct p9_qid **);
|
|
||||||
|
|
||||||
*wqids = NULL;
|
|
||||||
|
|
||||||
errcode =
|
|
||||||
p9pdu_readf(pdu, proto_version, "w", nwqid);
|
|
||||||
if (!errcode) {
|
|
||||||
*wqids =
|
|
||||||
kmalloc(*nwqid *
|
|
||||||
sizeof(struct p9_qid),
|
|
||||||
GFP_NOFS);
|
|
||||||
if (*wqids == NULL)
|
|
||||||
errcode = -ENOMEM;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (!errcode) {
|
|
||||||
int i;
|
|
||||||
|
|
||||||
for (i = 0; i < *nwqid; i++) {
|
|
||||||
errcode =
|
|
||||||
p9pdu_readf(pdu,
|
|
||||||
proto_version,
|
|
||||||
"Q",
|
|
||||||
&(*wqids)[i]);
|
|
||||||
if (errcode)
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if (errcode) {
|
|
||||||
kfree(*wqids);
|
|
||||||
*wqids = NULL;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
break;
|
|
||||||
case 'A': {
|
|
||||||
struct p9_stat_dotl *stbuf =
|
|
||||||
va_arg(ap, struct p9_stat_dotl *);
|
|
||||||
|
|
||||||
memset(stbuf, 0, sizeof(struct p9_stat_dotl));
|
|
||||||
errcode =
|
|
||||||
p9pdu_readf(pdu, proto_version,
|
|
||||||
"qQdugqqqqqqqqqqqqqqq",
|
|
||||||
&stbuf->st_result_mask,
|
|
||||||
&stbuf->qid,
|
|
||||||
&stbuf->st_mode,
|
|
||||||
&stbuf->st_uid, &stbuf->st_gid,
|
|
||||||
&stbuf->st_nlink,
|
|
||||||
&stbuf->st_rdev, &stbuf->st_size,
|
|
||||||
&stbuf->st_blksize, &stbuf->st_blocks,
|
|
||||||
&stbuf->st_atime_sec,
|
|
||||||
&stbuf->st_atime_nsec,
|
|
||||||
&stbuf->st_mtime_sec,
|
|
||||||
&stbuf->st_mtime_nsec,
|
|
||||||
&stbuf->st_ctime_sec,
|
|
||||||
&stbuf->st_ctime_nsec,
|
|
||||||
&stbuf->st_btime_sec,
|
|
||||||
&stbuf->st_btime_nsec,
|
|
||||||
&stbuf->st_gen,
|
|
||||||
&stbuf->st_data_version);
|
|
||||||
}
|
|
||||||
break;
|
|
||||||
case '?':
|
|
||||||
if ((proto_version != p9_proto_2000u) &&
|
|
||||||
(proto_version != p9_proto_2000L))
|
|
||||||
return 0;
|
|
||||||
break;
|
|
||||||
default:
|
|
||||||
BUG();
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (errcode)
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
|
|
||||||
return errcode;
|
|
||||||
}
|
|
||||||
|
|
||||||
int
|
|
||||||
p9pdu_vwritef(struct p9_fcall *pdu, int proto_version, const char *fmt,
|
|
||||||
va_list ap)
|
|
||||||
{
|
|
||||||
const char *ptr;
|
|
||||||
int errcode = 0;
|
|
||||||
|
|
||||||
for (ptr = fmt; *ptr; ptr++) {
|
|
||||||
switch (*ptr) {
|
|
||||||
case 'b':{
|
|
||||||
int8_t val = va_arg(ap, int);
|
|
||||||
if (pdu_write(pdu, &val, sizeof(val)))
|
|
||||||
errcode = -EFAULT;
|
|
||||||
}
|
|
||||||
break;
|
|
||||||
case 'w':{
|
|
||||||
__le16 val = cpu_to_le16(va_arg(ap, int));
|
|
||||||
if (pdu_write(pdu, &val, sizeof(val)))
|
|
||||||
errcode = -EFAULT;
|
|
||||||
}
|
|
||||||
break;
|
|
||||||
case 'd':{
|
|
||||||
__le32 val = cpu_to_le32(va_arg(ap, int32_t));
|
|
||||||
if (pdu_write(pdu, &val, sizeof(val)))
|
|
||||||
errcode = -EFAULT;
|
|
||||||
}
|
|
||||||
break;
|
|
||||||
case 'q':{
|
|
||||||
__le64 val = cpu_to_le64(va_arg(ap, int64_t));
|
|
||||||
if (pdu_write(pdu, &val, sizeof(val)))
|
|
||||||
errcode = -EFAULT;
|
|
||||||
}
|
|
||||||
break;
|
|
||||||
case 's':{
|
|
||||||
const char *sptr = va_arg(ap, const char *);
|
|
||||||
uint16_t len = 0;
|
|
||||||
if (sptr)
|
|
||||||
len = min_t(size_t, strlen(sptr),
|
|
||||||
USHRT_MAX);
|
|
||||||
|
|
||||||
errcode = p9pdu_writef(pdu, proto_version,
|
|
||||||
"w", len);
|
|
||||||
if (!errcode && pdu_write(pdu, sptr, len))
|
|
||||||
errcode = -EFAULT;
|
|
||||||
}
|
|
||||||
break;
|
|
||||||
case 'u': {
|
|
||||||
kuid_t uid = va_arg(ap, kuid_t);
|
|
||||||
__le32 val = cpu_to_le32(
|
|
||||||
from_kuid(&init_user_ns, uid));
|
|
||||||
if (pdu_write(pdu, &val, sizeof(val)))
|
|
||||||
errcode = -EFAULT;
|
|
||||||
} break;
|
|
||||||
case 'g': {
|
|
||||||
kgid_t gid = va_arg(ap, kgid_t);
|
|
||||||
__le32 val = cpu_to_le32(
|
|
||||||
from_kgid(&init_user_ns, gid));
|
|
||||||
if (pdu_write(pdu, &val, sizeof(val)))
|
|
||||||
errcode = -EFAULT;
|
|
||||||
} break;
|
|
||||||
case 'Q':{
|
|
||||||
const struct p9_qid *qid =
|
|
||||||
va_arg(ap, const struct p9_qid *);
|
|
||||||
errcode =
|
|
||||||
p9pdu_writef(pdu, proto_version, "bdq",
|
|
||||||
qid->type, qid->version,
|
|
||||||
qid->path);
|
|
||||||
} break;
|
|
||||||
case 'S':{
|
|
||||||
const struct p9_wstat *stbuf =
|
|
||||||
va_arg(ap, const struct p9_wstat *);
|
|
||||||
errcode =
|
|
||||||
p9pdu_writef(pdu, proto_version,
|
|
||||||
"wwdQdddqssss?sugu",
|
|
||||||
stbuf->size, stbuf->type,
|
|
||||||
stbuf->dev, &stbuf->qid,
|
|
||||||
stbuf->mode, stbuf->atime,
|
|
||||||
stbuf->mtime, stbuf->length,
|
|
||||||
stbuf->name, stbuf->uid,
|
|
||||||
stbuf->gid, stbuf->muid,
|
|
||||||
stbuf->extension, stbuf->n_uid,
|
|
||||||
stbuf->n_gid, stbuf->n_muid);
|
|
||||||
} break;
|
|
||||||
case 'D':{
|
|
||||||
uint32_t count = va_arg(ap, uint32_t);
|
|
||||||
const void *data = va_arg(ap, const void *);
|
|
||||||
|
|
||||||
errcode = p9pdu_writef(pdu, proto_version, "d",
|
|
||||||
count);
|
|
||||||
if (!errcode && pdu_write(pdu, data, count))
|
|
||||||
errcode = -EFAULT;
|
|
||||||
}
|
|
||||||
break;
|
|
||||||
case 'U':{
|
|
||||||
int32_t count = va_arg(ap, int32_t);
|
|
||||||
const char __user *udata =
|
|
||||||
va_arg(ap, const void __user *);
|
|
||||||
errcode = p9pdu_writef(pdu, proto_version, "d",
|
|
||||||
count);
|
|
||||||
if (!errcode && pdu_write_u(pdu, udata, count))
|
|
||||||
errcode = -EFAULT;
|
|
||||||
}
|
|
||||||
break;
|
|
||||||
case 'T':{
|
|
||||||
uint16_t nwname = va_arg(ap, int);
|
|
||||||
const char **wnames = va_arg(ap, const char **);
|
|
||||||
|
|
||||||
errcode = p9pdu_writef(pdu, proto_version, "w",
|
|
||||||
nwname);
|
|
||||||
if (!errcode) {
|
|
||||||
int i;
|
|
||||||
|
|
||||||
for (i = 0; i < nwname; i++) {
|
|
||||||
errcode =
|
|
||||||
p9pdu_writef(pdu,
|
|
||||||
proto_version,
|
|
||||||
"s",
|
|
||||||
wnames[i]);
|
|
||||||
if (errcode)
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
break;
|
|
||||||
case 'R':{
|
|
||||||
int16_t nwqid = va_arg(ap, int);
|
|
||||||
struct p9_qid *wqids =
|
|
||||||
va_arg(ap, struct p9_qid *);
|
|
||||||
|
|
||||||
errcode = p9pdu_writef(pdu, proto_version, "w",
|
|
||||||
nwqid);
|
|
||||||
if (!errcode) {
|
|
||||||
int i;
|
|
||||||
|
|
||||||
for (i = 0; i < nwqid; i++) {
|
|
||||||
errcode =
|
|
||||||
p9pdu_writef(pdu,
|
|
||||||
proto_version,
|
|
||||||
"Q",
|
|
||||||
&wqids[i]);
|
|
||||||
if (errcode)
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
break;
|
|
||||||
case 'I':{
|
|
||||||
struct p9_iattr_dotl *p9attr = va_arg(ap,
|
|
||||||
struct p9_iattr_dotl *);
|
|
||||||
|
|
||||||
errcode = p9pdu_writef(pdu, proto_version,
|
|
||||||
"ddugqqqqq",
|
|
||||||
p9attr->valid,
|
|
||||||
p9attr->mode,
|
|
||||||
p9attr->uid,
|
|
||||||
p9attr->gid,
|
|
||||||
p9attr->size,
|
|
||||||
p9attr->atime_sec,
|
|
||||||
p9attr->atime_nsec,
|
|
||||||
p9attr->mtime_sec,
|
|
||||||
p9attr->mtime_nsec);
|
|
||||||
}
|
|
||||||
break;
|
|
||||||
case '?':
|
|
||||||
if ((proto_version != p9_proto_2000u) &&
|
|
||||||
(proto_version != p9_proto_2000L))
|
|
||||||
return 0;
|
|
||||||
break;
|
|
||||||
default:
|
|
||||||
BUG();
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (errcode)
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
|
|
||||||
return errcode;
|
|
||||||
}
|
|
||||||
|
|
||||||
int p9pdu_readf(struct p9_fcall *pdu, int proto_version, const char *fmt, ...)
|
|
||||||
{
|
|
||||||
va_list ap;
|
|
||||||
int ret;
|
|
||||||
|
|
||||||
va_start(ap, fmt);
|
|
||||||
ret = p9pdu_vreadf(pdu, proto_version, fmt, ap);
|
|
||||||
va_end(ap);
|
|
||||||
|
|
||||||
return ret;
|
|
||||||
}
|
|
||||||
|
|
||||||
static int
|
|
||||||
p9pdu_writef(struct p9_fcall *pdu, int proto_version, const char *fmt, ...)
|
|
||||||
{
|
|
||||||
va_list ap;
|
|
||||||
int ret;
|
|
||||||
|
|
||||||
va_start(ap, fmt);
|
|
||||||
ret = p9pdu_vwritef(pdu, proto_version, fmt, ap);
|
|
||||||
va_end(ap);
|
|
||||||
|
|
||||||
return ret;
|
|
||||||
}
|
|
||||||
|
|
||||||
int p9stat_read(struct p9_client *clnt, char *buf, int len, struct p9_wstat *st)
|
|
||||||
{
|
|
||||||
struct p9_fcall fake_pdu;
|
|
||||||
int ret;
|
|
||||||
|
|
||||||
fake_pdu.size = len;
|
|
||||||
fake_pdu.capacity = len;
|
|
||||||
fake_pdu.sdata = buf;
|
|
||||||
fake_pdu.offset = 0;
|
|
||||||
|
|
||||||
ret = p9pdu_readf(&fake_pdu, clnt->proto_version, "S", st);
|
|
||||||
if (ret) {
|
|
||||||
p9_debug(P9_DEBUG_9P, "<<< p9stat_read failed: %d\n", ret);
|
|
||||||
trace_9p_protocol_dump(clnt, &fake_pdu);
|
|
||||||
}
|
|
||||||
|
|
||||||
return ret;
|
|
||||||
}
|
|
||||||
EXPORT_SYMBOL(p9stat_read);
|
|
||||||
|
|
||||||
int p9pdu_prepare(struct p9_fcall *pdu, int16_t tag, int8_t type)
|
|
||||||
{
|
|
||||||
pdu->id = type;
|
|
||||||
return p9pdu_writef(pdu, 0, "dbw", 0, type, tag);
|
|
||||||
}
|
|
||||||
|
|
||||||
int p9pdu_finalize(struct p9_client *clnt, struct p9_fcall *pdu)
|
|
||||||
{
|
|
||||||
int size = pdu->size;
|
|
||||||
int err;
|
|
||||||
|
|
||||||
pdu->size = 0;
|
|
||||||
err = p9pdu_writef(pdu, 0, "d", size);
|
|
||||||
pdu->size = size;
|
|
||||||
|
|
||||||
trace_9p_protocol_dump(clnt, pdu);
|
|
||||||
p9_debug(P9_DEBUG_9P, ">>> size=%d type: %d tag: %d\n",
|
|
||||||
pdu->size, pdu->id, pdu->tag);
|
|
||||||
|
|
||||||
return err;
|
|
||||||
}
|
|
||||||
|
|
||||||
void p9pdu_reset(struct p9_fcall *pdu)
|
|
||||||
{
|
|
||||||
pdu->offset = 0;
|
|
||||||
pdu->size = 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
int p9dirent_read(struct p9_client *clnt, char *buf, int len,
|
|
||||||
struct p9_dirent *dirent)
|
|
||||||
{
|
|
||||||
struct p9_fcall fake_pdu;
|
|
||||||
int ret;
|
|
||||||
char *nameptr;
|
|
||||||
|
|
||||||
fake_pdu.size = len;
|
|
||||||
fake_pdu.capacity = len;
|
|
||||||
fake_pdu.sdata = buf;
|
|
||||||
fake_pdu.offset = 0;
|
|
||||||
|
|
||||||
ret = p9pdu_readf(&fake_pdu, clnt->proto_version, "Qqbs", &dirent->qid,
|
|
||||||
&dirent->d_off, &dirent->d_type, &nameptr);
|
|
||||||
if (ret) {
|
|
||||||
p9_debug(P9_DEBUG_9P, "<<< p9dirent_read failed: %d\n", ret);
|
|
||||||
trace_9p_protocol_dump(clnt, &fake_pdu);
|
|
||||||
goto out;
|
|
||||||
}
|
|
||||||
|
|
||||||
strcpy(dirent->d_name, nameptr);
|
|
||||||
kfree(nameptr);
|
|
||||||
|
|
||||||
out:
|
|
||||||
return fake_pdu.offset;
|
|
||||||
}
|
|
||||||
EXPORT_SYMBOL(p9dirent_read);
|
|
@ -1,34 +0,0 @@
|
|||||||
/*
|
|
||||||
* net/9p/protocol.h
|
|
||||||
*
|
|
||||||
* 9P Protocol Support Code
|
|
||||||
*
|
|
||||||
* Copyright (C) 2008 by Eric Van Hensbergen <ericvh@gmail.com>
|
|
||||||
*
|
|
||||||
* Base on code from Anthony Liguori <aliguori@us.ibm.com>
|
|
||||||
* Copyright (C) 2008 by IBM, Corp.
|
|
||||||
*
|
|
||||||
* This program is free software; you can redistribute it and/or modify
|
|
||||||
* it under the terms of the GNU General Public License version 2
|
|
||||||
* as published by the Free Software Foundation.
|
|
||||||
*
|
|
||||||
* This program is distributed in the hope that it will be useful,
|
|
||||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
||||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
||||||
* GNU General Public License for more details.
|
|
||||||
*
|
|
||||||
* You should have received a copy of the GNU General Public License
|
|
||||||
* along with this program; if not, write to:
|
|
||||||
* Free Software Foundation
|
|
||||||
* 51 Franklin Street, Fifth Floor
|
|
||||||
* Boston, MA 02111-1301 USA
|
|
||||||
*
|
|
||||||
*/
|
|
||||||
|
|
||||||
int p9pdu_vwritef(struct p9_fcall *pdu, int proto_version, const char *fmt,
|
|
||||||
va_list ap);
|
|
||||||
int p9pdu_readf(struct p9_fcall *pdu, int proto_version, const char *fmt, ...);
|
|
||||||
int p9pdu_prepare(struct p9_fcall *pdu, int16_t tag, int8_t type);
|
|
||||||
int p9pdu_finalize(struct p9_client *clnt, struct p9_fcall *pdu);
|
|
||||||
void p9pdu_reset(struct p9_fcall *pdu);
|
|
||||||
size_t pdu_read(struct p9_fcall *pdu, void *data, size_t size);
|
|
@ -1,69 +0,0 @@
|
|||||||
/*
|
|
||||||
* Copyright IBM Corporation, 2010
|
|
||||||
* Author Venkateswararao Jujjuri <jvrao@linux.vnet.ibm.com>
|
|
||||||
*
|
|
||||||
* This program is free software; you can redistribute it and/or modify it
|
|
||||||
* under the terms of version 2.1 of the GNU Lesser General Public License
|
|
||||||
* as published by the Free Software Foundation.
|
|
||||||
*
|
|
||||||
* This program is distributed in the hope that it would be useful, but
|
|
||||||
* WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
||||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
|
|
||||||
*
|
|
||||||
*/
|
|
||||||
|
|
||||||
#include <linux/slab.h>
|
|
||||||
#include <linux/module.h>
|
|
||||||
#include <net/9p/9p.h>
|
|
||||||
#include <net/9p/client.h>
|
|
||||||
#include <linux/scatterlist.h>
|
|
||||||
#include "trans_common.h"
|
|
||||||
|
|
||||||
/**
|
|
||||||
* p9_release_req_pages - Release pages after the transaction.
|
|
||||||
*/
|
|
||||||
void p9_release_pages(struct page **pages, int nr_pages)
|
|
||||||
{
|
|
||||||
int i;
|
|
||||||
|
|
||||||
for (i = 0; i < nr_pages; i++)
|
|
||||||
if (pages[i])
|
|
||||||
put_page(pages[i]);
|
|
||||||
}
|
|
||||||
EXPORT_SYMBOL(p9_release_pages);
|
|
||||||
|
|
||||||
/**
|
|
||||||
* p9_nr_pages - Return number of pages needed to accommodate the payload.
|
|
||||||
*/
|
|
||||||
int p9_nr_pages(char *data, int len)
|
|
||||||
{
|
|
||||||
unsigned long start_page, end_page;
|
|
||||||
start_page = (unsigned long)data >> PAGE_SHIFT;
|
|
||||||
end_page = ((unsigned long)data + len + PAGE_SIZE - 1) >> PAGE_SHIFT;
|
|
||||||
return end_page - start_page;
|
|
||||||
}
|
|
||||||
EXPORT_SYMBOL(p9_nr_pages);
|
|
||||||
|
|
||||||
/**
|
|
||||||
* payload_gup - Translates user buffer into kernel pages and
|
|
||||||
* pins them either for read/write through get_user_pages_fast().
|
|
||||||
* @req: Request to be sent to server.
|
|
||||||
* @pdata_off: data offset into the first page after translation (gup).
|
|
||||||
* @pdata_len: Total length of the IO. gup may not return requested # of pages.
|
|
||||||
* @nr_pages: number of pages to accommodate the payload
|
|
||||||
* @rw: Indicates if the pages are for read or write.
|
|
||||||
*/
|
|
||||||
|
|
||||||
int p9_payload_gup(char *data, int *nr_pages, struct page **pages, int write)
|
|
||||||
{
|
|
||||||
int nr_mapped_pages;
|
|
||||||
|
|
||||||
nr_mapped_pages = get_user_pages_fast((unsigned long)data,
|
|
||||||
*nr_pages, write, pages);
|
|
||||||
if (nr_mapped_pages <= 0)
|
|
||||||
return nr_mapped_pages;
|
|
||||||
|
|
||||||
*nr_pages = nr_mapped_pages;
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
EXPORT_SYMBOL(p9_payload_gup);
|
|
@ -1,17 +0,0 @@
|
|||||||
/*
|
|
||||||
* Copyright IBM Corporation, 2010
|
|
||||||
* Author Venkateswararao Jujjuri <jvrao@linux.vnet.ibm.com>
|
|
||||||
*
|
|
||||||
* This program is free software; you can redistribute it and/or modify it
|
|
||||||
* under the terms of version 2.1 of the GNU Lesser General Public License
|
|
||||||
* as published by the Free Software Foundation.
|
|
||||||
*
|
|
||||||
* This program is distributed in the hope that it would be useful, but
|
|
||||||
* WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
||||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
|
|
||||||
*
|
|
||||||
*/
|
|
||||||
|
|
||||||
void p9_release_pages(struct page **, int);
|
|
||||||
int p9_payload_gup(char *, int *, struct page **, int);
|
|
||||||
int p9_nr_pages(char *, int);
|
|
File diff suppressed because it is too large
Load Diff
@ -1,719 +0,0 @@
|
|||||||
/*
|
|
||||||
* linux/fs/9p/trans_rdma.c
|
|
||||||
*
|
|
||||||
* RDMA transport layer based on the trans_fd.c implementation.
|
|
||||||
*
|
|
||||||
* Copyright (C) 2008 by Tom Tucker <tom@opengridcomputing.com>
|
|
||||||
* Copyright (C) 2006 by Russ Cox <rsc@swtch.com>
|
|
||||||
* Copyright (C) 2004-2005 by Latchesar Ionkov <lucho@ionkov.net>
|
|
||||||
* Copyright (C) 2004-2008 by Eric Van Hensbergen <ericvh@gmail.com>
|
|
||||||
* Copyright (C) 1997-2002 by Ron Minnich <rminnich@sarnoff.com>
|
|
||||||
*
|
|
||||||
* This program is free software; you can redistribute it and/or modify
|
|
||||||
* it under the terms of the GNU General Public License version 2
|
|
||||||
* as published by the Free Software Foundation.
|
|
||||||
*
|
|
||||||
* This program is distributed in the hope that it will be useful,
|
|
||||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
||||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
||||||
* GNU General Public License for more details.
|
|
||||||
*
|
|
||||||
* You should have received a copy of the GNU General Public License
|
|
||||||
* along with this program; if not, write to:
|
|
||||||
* Free Software Foundation
|
|
||||||
* 51 Franklin Street, Fifth Floor
|
|
||||||
* Boston, MA 02111-1301 USA
|
|
||||||
*
|
|
||||||
*/
|
|
||||||
|
|
||||||
#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
|
|
||||||
|
|
||||||
#include <linux/in.h>
|
|
||||||
#include <linux/module.h>
|
|
||||||
#include <linux/net.h>
|
|
||||||
#include <linux/ipv6.h>
|
|
||||||
#include <linux/kthread.h>
|
|
||||||
#include <linux/errno.h>
|
|
||||||
#include <linux/kernel.h>
|
|
||||||
#include <linux/un.h>
|
|
||||||
#include <linux/uaccess.h>
|
|
||||||
#include <linux/inet.h>
|
|
||||||
#include <linux/idr.h>
|
|
||||||
#include <linux/file.h>
|
|
||||||
#include <linux/parser.h>
|
|
||||||
#include <linux/semaphore.h>
|
|
||||||
#include <linux/slab.h>
|
|
||||||
#include <net/9p/9p.h>
|
|
||||||
#include <net/9p/client.h>
|
|
||||||
#include <net/9p/transport.h>
|
|
||||||
#include <rdma/ib_verbs.h>
|
|
||||||
#include <rdma/rdma_cm.h>
|
|
||||||
|
|
||||||
#define P9_PORT 5640
|
|
||||||
#define P9_RDMA_SQ_DEPTH 32
|
|
||||||
#define P9_RDMA_RQ_DEPTH 32
|
|
||||||
#define P9_RDMA_SEND_SGE 4
|
|
||||||
#define P9_RDMA_RECV_SGE 4
|
|
||||||
#define P9_RDMA_IRD 0
|
|
||||||
#define P9_RDMA_ORD 0
|
|
||||||
#define P9_RDMA_TIMEOUT 30000 /* 30 seconds */
|
|
||||||
#define P9_RDMA_MAXSIZE (4*4096) /* Min SGE is 4, so we can
|
|
||||||
* safely advertise a maxsize
|
|
||||||
* of 64k */
|
|
||||||
|
|
||||||
/**
|
|
||||||
* struct p9_trans_rdma - RDMA transport instance
|
|
||||||
*
|
|
||||||
* @state: tracks the transport state machine for connection setup and tear down
|
|
||||||
* @cm_id: The RDMA CM ID
|
|
||||||
* @pd: Protection Domain pointer
|
|
||||||
* @qp: Queue Pair pointer
|
|
||||||
* @cq: Completion Queue pointer
|
|
||||||
* @dm_mr: DMA Memory Region pointer
|
|
||||||
* @lkey: The local access only memory region key
|
|
||||||
* @timeout: Number of uSecs to wait for connection management events
|
|
||||||
* @sq_depth: The depth of the Send Queue
|
|
||||||
* @sq_sem: Semaphore for the SQ
|
|
||||||
* @rq_depth: The depth of the Receive Queue.
|
|
||||||
* @rq_count: Count of requests in the Receive Queue.
|
|
||||||
* @addr: The remote peer's address
|
|
||||||
* @req_lock: Protects the active request list
|
|
||||||
* @cm_done: Completion event for connection management tracking
|
|
||||||
*/
|
|
||||||
struct p9_trans_rdma {
|
|
||||||
enum {
|
|
||||||
P9_RDMA_INIT,
|
|
||||||
P9_RDMA_ADDR_RESOLVED,
|
|
||||||
P9_RDMA_ROUTE_RESOLVED,
|
|
||||||
P9_RDMA_CONNECTED,
|
|
||||||
P9_RDMA_FLUSHING,
|
|
||||||
P9_RDMA_CLOSING,
|
|
||||||
P9_RDMA_CLOSED,
|
|
||||||
} state;
|
|
||||||
struct rdma_cm_id *cm_id;
|
|
||||||
struct ib_pd *pd;
|
|
||||||
struct ib_qp *qp;
|
|
||||||
struct ib_cq *cq;
|
|
||||||
struct ib_mr *dma_mr;
|
|
||||||
u32 lkey;
|
|
||||||
long timeout;
|
|
||||||
int sq_depth;
|
|
||||||
struct semaphore sq_sem;
|
|
||||||
int rq_depth;
|
|
||||||
atomic_t rq_count;
|
|
||||||
struct sockaddr_in addr;
|
|
||||||
spinlock_t req_lock;
|
|
||||||
|
|
||||||
struct completion cm_done;
|
|
||||||
};
|
|
||||||
|
|
||||||
/**
|
|
||||||
* p9_rdma_context - Keeps track of in-process WR
|
|
||||||
*
|
|
||||||
* @wc_op: The original WR op for when the CQE completes in error.
|
|
||||||
* @busa: Bus address to unmap when the WR completes
|
|
||||||
* @req: Keeps track of requests (send)
|
|
||||||
* @rc: Keepts track of replies (receive)
|
|
||||||
*/
|
|
||||||
struct p9_rdma_req;
|
|
||||||
struct p9_rdma_context {
|
|
||||||
enum ib_wc_opcode wc_op;
|
|
||||||
dma_addr_t busa;
|
|
||||||
union {
|
|
||||||
struct p9_req_t *req;
|
|
||||||
struct p9_fcall *rc;
|
|
||||||
};
|
|
||||||
};
|
|
||||||
|
|
||||||
/**
|
|
||||||
* p9_rdma_opts - Collection of mount options
|
|
||||||
* @port: port of connection
|
|
||||||
* @sq_depth: The requested depth of the SQ. This really doesn't need
|
|
||||||
* to be any deeper than the number of threads used in the client
|
|
||||||
* @rq_depth: The depth of the RQ. Should be greater than or equal to SQ depth
|
|
||||||
* @timeout: Time to wait in msecs for CM events
|
|
||||||
*/
|
|
||||||
struct p9_rdma_opts {
|
|
||||||
short port;
|
|
||||||
int sq_depth;
|
|
||||||
int rq_depth;
|
|
||||||
long timeout;
|
|
||||||
};
|
|
||||||
|
|
||||||
/*
|
|
||||||
* Option Parsing (code inspired by NFS code)
|
|
||||||
*/
|
|
||||||
enum {
|
|
||||||
/* Options that take integer arguments */
|
|
||||||
Opt_port, Opt_rq_depth, Opt_sq_depth, Opt_timeout, Opt_err,
|
|
||||||
};
|
|
||||||
|
|
||||||
static match_table_t tokens = {
|
|
||||||
{Opt_port, "port=%u"},
|
|
||||||
{Opt_sq_depth, "sq=%u"},
|
|
||||||
{Opt_rq_depth, "rq=%u"},
|
|
||||||
{Opt_timeout, "timeout=%u"},
|
|
||||||
{Opt_err, NULL},
|
|
||||||
};
|
|
||||||
|
|
||||||
/**
|
|
||||||
* parse_opts - parse mount options into rdma options structure
|
|
||||||
* @params: options string passed from mount
|
|
||||||
* @opts: rdma transport-specific structure to parse options into
|
|
||||||
*
|
|
||||||
* Returns 0 upon success, -ERRNO upon failure
|
|
||||||
*/
|
|
||||||
static int parse_opts(char *params, struct p9_rdma_opts *opts)
|
|
||||||
{
|
|
||||||
char *p;
|
|
||||||
substring_t args[MAX_OPT_ARGS];
|
|
||||||
int option;
|
|
||||||
char *options, *tmp_options;
|
|
||||||
|
|
||||||
opts->port = P9_PORT;
|
|
||||||
opts->sq_depth = P9_RDMA_SQ_DEPTH;
|
|
||||||
opts->rq_depth = P9_RDMA_RQ_DEPTH;
|
|
||||||
opts->timeout = P9_RDMA_TIMEOUT;
|
|
||||||
|
|
||||||
if (!params)
|
|
||||||
return 0;
|
|
||||||
|
|
||||||
tmp_options = kstrdup(params, GFP_KERNEL);
|
|
||||||
if (!tmp_options) {
|
|
||||||
p9_debug(P9_DEBUG_ERROR,
|
|
||||||
"failed to allocate copy of option string\n");
|
|
||||||
return -ENOMEM;
|
|
||||||
}
|
|
||||||
options = tmp_options;
|
|
||||||
|
|
||||||
while ((p = strsep(&options, ",")) != NULL) {
|
|
||||||
int token;
|
|
||||||
int r;
|
|
||||||
if (!*p)
|
|
||||||
continue;
|
|
||||||
token = match_token(p, tokens, args);
|
|
||||||
r = match_int(&args[0], &option);
|
|
||||||
if (r < 0) {
|
|
||||||
p9_debug(P9_DEBUG_ERROR,
|
|
||||||
"integer field, but no integer?\n");
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
switch (token) {
|
|
||||||
case Opt_port:
|
|
||||||
opts->port = option;
|
|
||||||
break;
|
|
||||||
case Opt_sq_depth:
|
|
||||||
opts->sq_depth = option;
|
|
||||||
break;
|
|
||||||
case Opt_rq_depth:
|
|
||||||
opts->rq_depth = option;
|
|
||||||
break;
|
|
||||||
case Opt_timeout:
|
|
||||||
opts->timeout = option;
|
|
||||||
break;
|
|
||||||
default:
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
/* RQ must be at least as large as the SQ */
|
|
||||||
opts->rq_depth = max(opts->rq_depth, opts->sq_depth);
|
|
||||||
kfree(tmp_options);
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
static int
|
|
||||||
p9_cm_event_handler(struct rdma_cm_id *id, struct rdma_cm_event *event)
|
|
||||||
{
|
|
||||||
struct p9_client *c = id->context;
|
|
||||||
struct p9_trans_rdma *rdma = c->trans;
|
|
||||||
switch (event->event) {
|
|
||||||
case RDMA_CM_EVENT_ADDR_RESOLVED:
|
|
||||||
BUG_ON(rdma->state != P9_RDMA_INIT);
|
|
||||||
rdma->state = P9_RDMA_ADDR_RESOLVED;
|
|
||||||
break;
|
|
||||||
|
|
||||||
case RDMA_CM_EVENT_ROUTE_RESOLVED:
|
|
||||||
BUG_ON(rdma->state != P9_RDMA_ADDR_RESOLVED);
|
|
||||||
rdma->state = P9_RDMA_ROUTE_RESOLVED;
|
|
||||||
break;
|
|
||||||
|
|
||||||
case RDMA_CM_EVENT_ESTABLISHED:
|
|
||||||
BUG_ON(rdma->state != P9_RDMA_ROUTE_RESOLVED);
|
|
||||||
rdma->state = P9_RDMA_CONNECTED;
|
|
||||||
break;
|
|
||||||
|
|
||||||
case RDMA_CM_EVENT_DISCONNECTED:
|
|
||||||
if (rdma)
|
|
||||||
rdma->state = P9_RDMA_CLOSED;
|
|
||||||
if (c)
|
|
||||||
c->status = Disconnected;
|
|
||||||
break;
|
|
||||||
|
|
||||||
case RDMA_CM_EVENT_TIMEWAIT_EXIT:
|
|
||||||
break;
|
|
||||||
|
|
||||||
case RDMA_CM_EVENT_ADDR_CHANGE:
|
|
||||||
case RDMA_CM_EVENT_ROUTE_ERROR:
|
|
||||||
case RDMA_CM_EVENT_DEVICE_REMOVAL:
|
|
||||||
case RDMA_CM_EVENT_MULTICAST_JOIN:
|
|
||||||
case RDMA_CM_EVENT_MULTICAST_ERROR:
|
|
||||||
case RDMA_CM_EVENT_REJECTED:
|
|
||||||
case RDMA_CM_EVENT_CONNECT_REQUEST:
|
|
||||||
case RDMA_CM_EVENT_CONNECT_RESPONSE:
|
|
||||||
case RDMA_CM_EVENT_CONNECT_ERROR:
|
|
||||||
case RDMA_CM_EVENT_ADDR_ERROR:
|
|
||||||
case RDMA_CM_EVENT_UNREACHABLE:
|
|
||||||
c->status = Disconnected;
|
|
||||||
rdma_disconnect(rdma->cm_id);
|
|
||||||
break;
|
|
||||||
default:
|
|
||||||
BUG();
|
|
||||||
}
|
|
||||||
complete(&rdma->cm_done);
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
static void
|
|
||||||
handle_recv(struct p9_client *client, struct p9_trans_rdma *rdma,
|
|
||||||
struct p9_rdma_context *c, enum ib_wc_status status, u32 byte_len)
|
|
||||||
{
|
|
||||||
struct p9_req_t *req;
|
|
||||||
int err = 0;
|
|
||||||
int16_t tag;
|
|
||||||
|
|
||||||
req = NULL;
|
|
||||||
ib_dma_unmap_single(rdma->cm_id->device, c->busa, client->msize,
|
|
||||||
DMA_FROM_DEVICE);
|
|
||||||
|
|
||||||
if (status != IB_WC_SUCCESS)
|
|
||||||
goto err_out;
|
|
||||||
|
|
||||||
err = p9_parse_header(c->rc, NULL, NULL, &tag, 1);
|
|
||||||
if (err)
|
|
||||||
goto err_out;
|
|
||||||
|
|
||||||
req = p9_tag_lookup(client, tag);
|
|
||||||
if (!req)
|
|
||||||
goto err_out;
|
|
||||||
|
|
||||||
req->rc = c->rc;
|
|
||||||
req->status = REQ_STATUS_RCVD;
|
|
||||||
p9_client_cb(client, req);
|
|
||||||
|
|
||||||
return;
|
|
||||||
|
|
||||||
err_out:
|
|
||||||
p9_debug(P9_DEBUG_ERROR, "req %p err %d status %d\n", req, err, status);
|
|
||||||
rdma->state = P9_RDMA_FLUSHING;
|
|
||||||
client->status = Disconnected;
|
|
||||||
}
|
|
||||||
|
|
||||||
static void
|
|
||||||
handle_send(struct p9_client *client, struct p9_trans_rdma *rdma,
|
|
||||||
struct p9_rdma_context *c, enum ib_wc_status status, u32 byte_len)
|
|
||||||
{
|
|
||||||
ib_dma_unmap_single(rdma->cm_id->device,
|
|
||||||
c->busa, c->req->tc->size,
|
|
||||||
DMA_TO_DEVICE);
|
|
||||||
}
|
|
||||||
|
|
||||||
static void qp_event_handler(struct ib_event *event, void *context)
|
|
||||||
{
|
|
||||||
p9_debug(P9_DEBUG_ERROR, "QP event %d context %p\n",
|
|
||||||
event->event, context);
|
|
||||||
}
|
|
||||||
|
|
||||||
static void cq_comp_handler(struct ib_cq *cq, void *cq_context)
|
|
||||||
{
|
|
||||||
struct p9_client *client = cq_context;
|
|
||||||
struct p9_trans_rdma *rdma = client->trans;
|
|
||||||
int ret;
|
|
||||||
struct ib_wc wc;
|
|
||||||
|
|
||||||
ib_req_notify_cq(rdma->cq, IB_CQ_NEXT_COMP);
|
|
||||||
while ((ret = ib_poll_cq(cq, 1, &wc)) > 0) {
|
|
||||||
struct p9_rdma_context *c = (void *) (unsigned long) wc.wr_id;
|
|
||||||
|
|
||||||
switch (c->wc_op) {
|
|
||||||
case IB_WC_RECV:
|
|
||||||
atomic_dec(&rdma->rq_count);
|
|
||||||
handle_recv(client, rdma, c, wc.status, wc.byte_len);
|
|
||||||
break;
|
|
||||||
|
|
||||||
case IB_WC_SEND:
|
|
||||||
handle_send(client, rdma, c, wc.status, wc.byte_len);
|
|
||||||
up(&rdma->sq_sem);
|
|
||||||
break;
|
|
||||||
|
|
||||||
default:
|
|
||||||
pr_err("unexpected completion type, c->wc_op=%d, wc.opcode=%d, status=%d\n",
|
|
||||||
c->wc_op, wc.opcode, wc.status);
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
kfree(c);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
static void cq_event_handler(struct ib_event *e, void *v)
|
|
||||||
{
|
|
||||||
p9_debug(P9_DEBUG_ERROR, "CQ event %d context %p\n", e->event, v);
|
|
||||||
}
|
|
||||||
|
|
||||||
static void rdma_destroy_trans(struct p9_trans_rdma *rdma)
|
|
||||||
{
|
|
||||||
if (!rdma)
|
|
||||||
return;
|
|
||||||
|
|
||||||
if (rdma->dma_mr && !IS_ERR(rdma->dma_mr))
|
|
||||||
ib_dereg_mr(rdma->dma_mr);
|
|
||||||
|
|
||||||
if (rdma->qp && !IS_ERR(rdma->qp))
|
|
||||||
ib_destroy_qp(rdma->qp);
|
|
||||||
|
|
||||||
if (rdma->pd && !IS_ERR(rdma->pd))
|
|
||||||
ib_dealloc_pd(rdma->pd);
|
|
||||||
|
|
||||||
if (rdma->cq && !IS_ERR(rdma->cq))
|
|
||||||
ib_destroy_cq(rdma->cq);
|
|
||||||
|
|
||||||
if (rdma->cm_id && !IS_ERR(rdma->cm_id))
|
|
||||||
rdma_destroy_id(rdma->cm_id);
|
|
||||||
|
|
||||||
kfree(rdma);
|
|
||||||
}
|
|
||||||
|
|
||||||
static int
|
|
||||||
post_recv(struct p9_client *client, struct p9_rdma_context *c)
|
|
||||||
{
|
|
||||||
struct p9_trans_rdma *rdma = client->trans;
|
|
||||||
struct ib_recv_wr wr, *bad_wr;
|
|
||||||
struct ib_sge sge;
|
|
||||||
|
|
||||||
c->busa = ib_dma_map_single(rdma->cm_id->device,
|
|
||||||
c->rc->sdata, client->msize,
|
|
||||||
DMA_FROM_DEVICE);
|
|
||||||
if (ib_dma_mapping_error(rdma->cm_id->device, c->busa))
|
|
||||||
goto error;
|
|
||||||
|
|
||||||
sge.addr = c->busa;
|
|
||||||
sge.length = client->msize;
|
|
||||||
sge.lkey = rdma->lkey;
|
|
||||||
|
|
||||||
wr.next = NULL;
|
|
||||||
c->wc_op = IB_WC_RECV;
|
|
||||||
wr.wr_id = (unsigned long) c;
|
|
||||||
wr.sg_list = &sge;
|
|
||||||
wr.num_sge = 1;
|
|
||||||
return ib_post_recv(rdma->qp, &wr, &bad_wr);
|
|
||||||
|
|
||||||
error:
|
|
||||||
p9_debug(P9_DEBUG_ERROR, "EIO\n");
|
|
||||||
return -EIO;
|
|
||||||
}
|
|
||||||
|
|
||||||
static int rdma_request(struct p9_client *client, struct p9_req_t *req)
|
|
||||||
{
|
|
||||||
struct p9_trans_rdma *rdma = client->trans;
|
|
||||||
struct ib_send_wr wr, *bad_wr;
|
|
||||||
struct ib_sge sge;
|
|
||||||
int err = 0;
|
|
||||||
unsigned long flags;
|
|
||||||
struct p9_rdma_context *c = NULL;
|
|
||||||
struct p9_rdma_context *rpl_context = NULL;
|
|
||||||
|
|
||||||
/* Allocate an fcall for the reply */
|
|
||||||
rpl_context = kmalloc(sizeof *rpl_context, GFP_NOFS);
|
|
||||||
if (!rpl_context) {
|
|
||||||
err = -ENOMEM;
|
|
||||||
goto err_close;
|
|
||||||
}
|
|
||||||
|
|
||||||
/*
|
|
||||||
* If the request has a buffer, steal it, otherwise
|
|
||||||
* allocate a new one. Typically, requests should already
|
|
||||||
* have receive buffers allocated and just swap them around
|
|
||||||
*/
|
|
||||||
if (!req->rc) {
|
|
||||||
req->rc = kmalloc(sizeof(struct p9_fcall)+client->msize,
|
|
||||||
GFP_NOFS);
|
|
||||||
if (req->rc) {
|
|
||||||
req->rc->sdata = (char *) req->rc +
|
|
||||||
sizeof(struct p9_fcall);
|
|
||||||
req->rc->capacity = client->msize;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
rpl_context->rc = req->rc;
|
|
||||||
if (!rpl_context->rc) {
|
|
||||||
err = -ENOMEM;
|
|
||||||
goto err_free2;
|
|
||||||
}
|
|
||||||
|
|
||||||
/*
|
|
||||||
* Post a receive buffer for this request. We need to ensure
|
|
||||||
* there is a reply buffer available for every outstanding
|
|
||||||
* request. A flushed request can result in no reply for an
|
|
||||||
* outstanding request, so we must keep a count to avoid
|
|
||||||
* overflowing the RQ.
|
|
||||||
*/
|
|
||||||
if (atomic_inc_return(&rdma->rq_count) <= rdma->rq_depth) {
|
|
||||||
err = post_recv(client, rpl_context);
|
|
||||||
if (err)
|
|
||||||
goto err_free1;
|
|
||||||
} else
|
|
||||||
atomic_dec(&rdma->rq_count);
|
|
||||||
|
|
||||||
/* remove posted receive buffer from request structure */
|
|
||||||
req->rc = NULL;
|
|
||||||
|
|
||||||
/* Post the request */
|
|
||||||
c = kmalloc(sizeof *c, GFP_NOFS);
|
|
||||||
if (!c) {
|
|
||||||
err = -ENOMEM;
|
|
||||||
goto err_free1;
|
|
||||||
}
|
|
||||||
c->req = req;
|
|
||||||
|
|
||||||
c->busa = ib_dma_map_single(rdma->cm_id->device,
|
|
||||||
c->req->tc->sdata, c->req->tc->size,
|
|
||||||
DMA_TO_DEVICE);
|
|
||||||
if (ib_dma_mapping_error(rdma->cm_id->device, c->busa))
|
|
||||||
goto error;
|
|
||||||
|
|
||||||
sge.addr = c->busa;
|
|
||||||
sge.length = c->req->tc->size;
|
|
||||||
sge.lkey = rdma->lkey;
|
|
||||||
|
|
||||||
wr.next = NULL;
|
|
||||||
c->wc_op = IB_WC_SEND;
|
|
||||||
wr.wr_id = (unsigned long) c;
|
|
||||||
wr.opcode = IB_WR_SEND;
|
|
||||||
wr.send_flags = IB_SEND_SIGNALED;
|
|
||||||
wr.sg_list = &sge;
|
|
||||||
wr.num_sge = 1;
|
|
||||||
|
|
||||||
if (down_interruptible(&rdma->sq_sem))
|
|
||||||
goto error;
|
|
||||||
|
|
||||||
return ib_post_send(rdma->qp, &wr, &bad_wr);
|
|
||||||
|
|
||||||
error:
|
|
||||||
kfree(c);
|
|
||||||
kfree(rpl_context->rc);
|
|
||||||
kfree(rpl_context);
|
|
||||||
p9_debug(P9_DEBUG_ERROR, "EIO\n");
|
|
||||||
return -EIO;
|
|
||||||
err_free1:
|
|
||||||
kfree(rpl_context->rc);
|
|
||||||
err_free2:
|
|
||||||
kfree(rpl_context);
|
|
||||||
err_close:
|
|
||||||
spin_lock_irqsave(&rdma->req_lock, flags);
|
|
||||||
if (rdma->state < P9_RDMA_CLOSING) {
|
|
||||||
rdma->state = P9_RDMA_CLOSING;
|
|
||||||
spin_unlock_irqrestore(&rdma->req_lock, flags);
|
|
||||||
rdma_disconnect(rdma->cm_id);
|
|
||||||
} else
|
|
||||||
spin_unlock_irqrestore(&rdma->req_lock, flags);
|
|
||||||
return err;
|
|
||||||
}
|
|
||||||
|
|
||||||
static void rdma_close(struct p9_client *client)
|
|
||||||
{
|
|
||||||
struct p9_trans_rdma *rdma;
|
|
||||||
|
|
||||||
if (!client)
|
|
||||||
return;
|
|
||||||
|
|
||||||
rdma = client->trans;
|
|
||||||
if (!rdma)
|
|
||||||
return;
|
|
||||||
|
|
||||||
client->status = Disconnected;
|
|
||||||
rdma_disconnect(rdma->cm_id);
|
|
||||||
rdma_destroy_trans(rdma);
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* alloc_rdma - Allocate and initialize the rdma transport structure
|
|
||||||
* @opts: Mount options structure
|
|
||||||
*/
|
|
||||||
static struct p9_trans_rdma *alloc_rdma(struct p9_rdma_opts *opts)
|
|
||||||
{
|
|
||||||
struct p9_trans_rdma *rdma;
|
|
||||||
|
|
||||||
rdma = kzalloc(sizeof(struct p9_trans_rdma), GFP_KERNEL);
|
|
||||||
if (!rdma)
|
|
||||||
return NULL;
|
|
||||||
|
|
||||||
rdma->sq_depth = opts->sq_depth;
|
|
||||||
rdma->rq_depth = opts->rq_depth;
|
|
||||||
rdma->timeout = opts->timeout;
|
|
||||||
spin_lock_init(&rdma->req_lock);
|
|
||||||
init_completion(&rdma->cm_done);
|
|
||||||
sema_init(&rdma->sq_sem, rdma->sq_depth);
|
|
||||||
atomic_set(&rdma->rq_count, 0);
|
|
||||||
|
|
||||||
return rdma;
|
|
||||||
}
|
|
||||||
|
|
||||||
/* its not clear to me we can do anything after send has been posted */
|
|
||||||
static int rdma_cancel(struct p9_client *client, struct p9_req_t *req)
|
|
||||||
{
|
|
||||||
return 1;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* trans_create_rdma - Transport method for creating atransport instance
|
|
||||||
* @client: client instance
|
|
||||||
* @addr: IP address string
|
|
||||||
* @args: Mount options string
|
|
||||||
*/
|
|
||||||
static int
|
|
||||||
rdma_create_trans(struct p9_client *client, const char *addr, char *args)
|
|
||||||
{
|
|
||||||
int err;
|
|
||||||
struct p9_rdma_opts opts;
|
|
||||||
struct p9_trans_rdma *rdma;
|
|
||||||
struct rdma_conn_param conn_param;
|
|
||||||
struct ib_qp_init_attr qp_attr;
|
|
||||||
struct ib_device_attr devattr;
|
|
||||||
|
|
||||||
/* Parse the transport specific mount options */
|
|
||||||
err = parse_opts(args, &opts);
|
|
||||||
if (err < 0)
|
|
||||||
return err;
|
|
||||||
|
|
||||||
/* Create and initialize the RDMA transport structure */
|
|
||||||
rdma = alloc_rdma(&opts);
|
|
||||||
if (!rdma)
|
|
||||||
return -ENOMEM;
|
|
||||||
|
|
||||||
/* Create the RDMA CM ID */
|
|
||||||
rdma->cm_id = rdma_create_id(p9_cm_event_handler, client, RDMA_PS_TCP,
|
|
||||||
IB_QPT_RC);
|
|
||||||
if (IS_ERR(rdma->cm_id))
|
|
||||||
goto error;
|
|
||||||
|
|
||||||
/* Associate the client with the transport */
|
|
||||||
client->trans = rdma;
|
|
||||||
|
|
||||||
/* Resolve the server's address */
|
|
||||||
rdma->addr.sin_family = AF_INET;
|
|
||||||
rdma->addr.sin_addr.s_addr = in_aton(addr);
|
|
||||||
rdma->addr.sin_port = htons(opts.port);
|
|
||||||
err = rdma_resolve_addr(rdma->cm_id, NULL,
|
|
||||||
(struct sockaddr *)&rdma->addr,
|
|
||||||
rdma->timeout);
|
|
||||||
if (err)
|
|
||||||
goto error;
|
|
||||||
err = wait_for_completion_interruptible(&rdma->cm_done);
|
|
||||||
if (err || (rdma->state != P9_RDMA_ADDR_RESOLVED))
|
|
||||||
goto error;
|
|
||||||
|
|
||||||
/* Resolve the route to the server */
|
|
||||||
err = rdma_resolve_route(rdma->cm_id, rdma->timeout);
|
|
||||||
if (err)
|
|
||||||
goto error;
|
|
||||||
err = wait_for_completion_interruptible(&rdma->cm_done);
|
|
||||||
if (err || (rdma->state != P9_RDMA_ROUTE_RESOLVED))
|
|
||||||
goto error;
|
|
||||||
|
|
||||||
/* Query the device attributes */
|
|
||||||
err = ib_query_device(rdma->cm_id->device, &devattr);
|
|
||||||
if (err)
|
|
||||||
goto error;
|
|
||||||
|
|
||||||
/* Create the Completion Queue */
|
|
||||||
rdma->cq = ib_create_cq(rdma->cm_id->device, cq_comp_handler,
|
|
||||||
cq_event_handler, client,
|
|
||||||
opts.sq_depth + opts.rq_depth + 1, 0);
|
|
||||||
if (IS_ERR(rdma->cq))
|
|
||||||
goto error;
|
|
||||||
ib_req_notify_cq(rdma->cq, IB_CQ_NEXT_COMP);
|
|
||||||
|
|
||||||
/* Create the Protection Domain */
|
|
||||||
rdma->pd = ib_alloc_pd(rdma->cm_id->device);
|
|
||||||
if (IS_ERR(rdma->pd))
|
|
||||||
goto error;
|
|
||||||
|
|
||||||
/* Cache the DMA lkey in the transport */
|
|
||||||
rdma->dma_mr = NULL;
|
|
||||||
if (devattr.device_cap_flags & IB_DEVICE_LOCAL_DMA_LKEY)
|
|
||||||
rdma->lkey = rdma->cm_id->device->local_dma_lkey;
|
|
||||||
else {
|
|
||||||
rdma->dma_mr = ib_get_dma_mr(rdma->pd, IB_ACCESS_LOCAL_WRITE);
|
|
||||||
if (IS_ERR(rdma->dma_mr))
|
|
||||||
goto error;
|
|
||||||
rdma->lkey = rdma->dma_mr->lkey;
|
|
||||||
}
|
|
||||||
|
|
||||||
/* Create the Queue Pair */
|
|
||||||
memset(&qp_attr, 0, sizeof qp_attr);
|
|
||||||
qp_attr.event_handler = qp_event_handler;
|
|
||||||
qp_attr.qp_context = client;
|
|
||||||
qp_attr.cap.max_send_wr = opts.sq_depth;
|
|
||||||
qp_attr.cap.max_recv_wr = opts.rq_depth;
|
|
||||||
qp_attr.cap.max_send_sge = P9_RDMA_SEND_SGE;
|
|
||||||
qp_attr.cap.max_recv_sge = P9_RDMA_RECV_SGE;
|
|
||||||
qp_attr.sq_sig_type = IB_SIGNAL_REQ_WR;
|
|
||||||
qp_attr.qp_type = IB_QPT_RC;
|
|
||||||
qp_attr.send_cq = rdma->cq;
|
|
||||||
qp_attr.recv_cq = rdma->cq;
|
|
||||||
err = rdma_create_qp(rdma->cm_id, rdma->pd, &qp_attr);
|
|
||||||
if (err)
|
|
||||||
goto error;
|
|
||||||
rdma->qp = rdma->cm_id->qp;
|
|
||||||
|
|
||||||
/* Request a connection */
|
|
||||||
memset(&conn_param, 0, sizeof(conn_param));
|
|
||||||
conn_param.private_data = NULL;
|
|
||||||
conn_param.private_data_len = 0;
|
|
||||||
conn_param.responder_resources = P9_RDMA_IRD;
|
|
||||||
conn_param.initiator_depth = P9_RDMA_ORD;
|
|
||||||
err = rdma_connect(rdma->cm_id, &conn_param);
|
|
||||||
if (err)
|
|
||||||
goto error;
|
|
||||||
err = wait_for_completion_interruptible(&rdma->cm_done);
|
|
||||||
if (err || (rdma->state != P9_RDMA_CONNECTED))
|
|
||||||
goto error;
|
|
||||||
|
|
||||||
client->status = Connected;
|
|
||||||
|
|
||||||
return 0;
|
|
||||||
|
|
||||||
error:
|
|
||||||
rdma_destroy_trans(rdma);
|
|
||||||
return -ENOTCONN;
|
|
||||||
}
|
|
||||||
|
|
||||||
static struct p9_trans_module p9_rdma_trans = {
|
|
||||||
.name = "rdma",
|
|
||||||
.maxsize = P9_RDMA_MAXSIZE,
|
|
||||||
.def = 0,
|
|
||||||
.owner = THIS_MODULE,
|
|
||||||
.create = rdma_create_trans,
|
|
||||||
.close = rdma_close,
|
|
||||||
.request = rdma_request,
|
|
||||||
.cancel = rdma_cancel,
|
|
||||||
};
|
|
||||||
|
|
||||||
/**
|
|
||||||
* p9_trans_rdma_init - Register the 9P RDMA transport driver
|
|
||||||
*/
|
|
||||||
static int __init p9_trans_rdma_init(void)
|
|
||||||
{
|
|
||||||
v9fs_register_trans(&p9_rdma_trans);
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
static void __exit p9_trans_rdma_exit(void)
|
|
||||||
{
|
|
||||||
v9fs_unregister_trans(&p9_rdma_trans);
|
|
||||||
}
|
|
||||||
|
|
||||||
module_init(p9_trans_rdma_init);
|
|
||||||
module_exit(p9_trans_rdma_exit);
|
|
||||||
|
|
||||||
MODULE_AUTHOR("Tom Tucker <tom@opengridcomputing.com>");
|
|
||||||
MODULE_DESCRIPTION("RDMA Transport for 9P");
|
|
||||||
MODULE_LICENSE("Dual BSD/GPL");
|
|
@ -1,730 +0,0 @@
|
|||||||
/*
|
|
||||||
* The Virtio 9p transport driver
|
|
||||||
*
|
|
||||||
* This is a block based transport driver based on the lguest block driver
|
|
||||||
* code.
|
|
||||||
*
|
|
||||||
* Copyright (C) 2007, 2008 Eric Van Hensbergen, IBM Corporation
|
|
||||||
*
|
|
||||||
* Based on virtio console driver
|
|
||||||
* Copyright (C) 2006, 2007 Rusty Russell, IBM Corporation
|
|
||||||
*
|
|
||||||
* This program is free software; you can redistribute it and/or modify
|
|
||||||
* it under the terms of the GNU General Public License version 2
|
|
||||||
* as published by the Free Software Foundation.
|
|
||||||
*
|
|
||||||
* This program is distributed in the hope that it will be useful,
|
|
||||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
||||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
||||||
* GNU General Public License for more details.
|
|
||||||
*
|
|
||||||
* You should have received a copy of the GNU General Public License
|
|
||||||
* along with this program; if not, write to:
|
|
||||||
* Free Software Foundation
|
|
||||||
* 51 Franklin Street, Fifth Floor
|
|
||||||
* Boston, MA 02111-1301 USA
|
|
||||||
*
|
|
||||||
*/
|
|
||||||
|
|
||||||
#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
|
|
||||||
|
|
||||||
#include <linux/in.h>
|
|
||||||
#include <linux/module.h>
|
|
||||||
#include <linux/net.h>
|
|
||||||
#include <linux/ipv6.h>
|
|
||||||
#include <linux/errno.h>
|
|
||||||
#include <linux/kernel.h>
|
|
||||||
#include <linux/un.h>
|
|
||||||
#include <linux/uaccess.h>
|
|
||||||
#include <linux/inet.h>
|
|
||||||
#include <linux/idr.h>
|
|
||||||
#include <linux/file.h>
|
|
||||||
#include <linux/highmem.h>
|
|
||||||
#include <linux/slab.h>
|
|
||||||
#include <net/9p/9p.h>
|
|
||||||
#include <linux/parser.h>
|
|
||||||
#include <net/9p/client.h>
|
|
||||||
#include <net/9p/transport.h>
|
|
||||||
#include <linux/scatterlist.h>
|
|
||||||
#include <linux/swap.h>
|
|
||||||
#include <linux/virtio.h>
|
|
||||||
#include <linux/virtio_9p.h>
|
|
||||||
#include "trans_common.h"
|
|
||||||
|
|
||||||
#define VIRTQUEUE_NUM 128
|
|
||||||
|
|
||||||
/* a single mutex to manage channel initialization and attachment */
|
|
||||||
static DEFINE_MUTEX(virtio_9p_lock);
|
|
||||||
static DECLARE_WAIT_QUEUE_HEAD(vp_wq);
|
|
||||||
static atomic_t vp_pinned = ATOMIC_INIT(0);
|
|
||||||
|
|
||||||
/**
|
|
||||||
* struct virtio_chan - per-instance transport information
|
|
||||||
* @initialized: whether the channel is initialized
|
|
||||||
* @inuse: whether the channel is in use
|
|
||||||
* @lock: protects multiple elements within this structure
|
|
||||||
* @client: client instance
|
|
||||||
* @vdev: virtio dev associated with this channel
|
|
||||||
* @vq: virtio queue associated with this channel
|
|
||||||
* @sg: scatter gather list which is used to pack a request (protected?)
|
|
||||||
*
|
|
||||||
* We keep all per-channel information in a structure.
|
|
||||||
* This structure is allocated within the devices dev->mem space.
|
|
||||||
* A pointer to the structure will get put in the transport private.
|
|
||||||
*
|
|
||||||
*/
|
|
||||||
|
|
||||||
struct virtio_chan {
|
|
||||||
bool inuse;
|
|
||||||
|
|
||||||
spinlock_t lock;
|
|
||||||
|
|
||||||
struct p9_client *client;
|
|
||||||
struct virtio_device *vdev;
|
|
||||||
struct virtqueue *vq;
|
|
||||||
int ring_bufs_avail;
|
|
||||||
wait_queue_head_t *vc_wq;
|
|
||||||
/* This is global limit. Since we don't have a global structure,
|
|
||||||
* will be placing it in each channel.
|
|
||||||
*/
|
|
||||||
unsigned long p9_max_pages;
|
|
||||||
/* Scatterlist: can be too big for stack. */
|
|
||||||
struct scatterlist sg[VIRTQUEUE_NUM];
|
|
||||||
|
|
||||||
int tag_len;
|
|
||||||
/*
|
|
||||||
* tag name to identify a mount Non-null terminated
|
|
||||||
*/
|
|
||||||
char *tag;
|
|
||||||
|
|
||||||
struct list_head chan_list;
|
|
||||||
};
|
|
||||||
|
|
||||||
static struct list_head virtio_chan_list;
|
|
||||||
|
|
||||||
/* How many bytes left in this page. */
|
|
||||||
static unsigned int rest_of_page(void *data)
|
|
||||||
{
|
|
||||||
return PAGE_SIZE - ((unsigned long)data % PAGE_SIZE);
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* p9_virtio_close - reclaim resources of a channel
|
|
||||||
* @client: client instance
|
|
||||||
*
|
|
||||||
* This reclaims a channel by freeing its resources and
|
|
||||||
* reseting its inuse flag.
|
|
||||||
*
|
|
||||||
*/
|
|
||||||
|
|
||||||
static void p9_virtio_close(struct p9_client *client)
|
|
||||||
{
|
|
||||||
struct virtio_chan *chan = client->trans;
|
|
||||||
|
|
||||||
mutex_lock(&virtio_9p_lock);
|
|
||||||
if (chan)
|
|
||||||
chan->inuse = false;
|
|
||||||
mutex_unlock(&virtio_9p_lock);
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* req_done - callback which signals activity from the server
|
|
||||||
* @vq: virtio queue activity was received on
|
|
||||||
*
|
|
||||||
* This notifies us that the server has triggered some activity
|
|
||||||
* on the virtio channel - most likely a response to request we
|
|
||||||
* sent. Figure out which requests now have responses and wake up
|
|
||||||
* those threads.
|
|
||||||
*
|
|
||||||
* Bugs: could do with some additional sanity checking, but appears to work.
|
|
||||||
*
|
|
||||||
*/
|
|
||||||
|
|
||||||
static void req_done(struct virtqueue *vq)
|
|
||||||
{
|
|
||||||
struct virtio_chan *chan = vq->vdev->priv;
|
|
||||||
struct p9_fcall *rc;
|
|
||||||
unsigned int len;
|
|
||||||
struct p9_req_t *req;
|
|
||||||
unsigned long flags;
|
|
||||||
|
|
||||||
p9_debug(P9_DEBUG_TRANS, ": request done\n");
|
|
||||||
|
|
||||||
while (1) {
|
|
||||||
spin_lock_irqsave(&chan->lock, flags);
|
|
||||||
rc = virtqueue_get_buf(chan->vq, &len);
|
|
||||||
if (rc == NULL) {
|
|
||||||
spin_unlock_irqrestore(&chan->lock, flags);
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
chan->ring_bufs_avail = 1;
|
|
||||||
spin_unlock_irqrestore(&chan->lock, flags);
|
|
||||||
/* Wakeup if anyone waiting for VirtIO ring space. */
|
|
||||||
wake_up(chan->vc_wq);
|
|
||||||
p9_debug(P9_DEBUG_TRANS, ": rc %p\n", rc);
|
|
||||||
p9_debug(P9_DEBUG_TRANS, ": lookup tag %d\n", rc->tag);
|
|
||||||
req = p9_tag_lookup(chan->client, rc->tag);
|
|
||||||
req->status = REQ_STATUS_RCVD;
|
|
||||||
p9_client_cb(chan->client, req);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* pack_sg_list - pack a scatter gather list from a linear buffer
|
|
||||||
* @sg: scatter/gather list to pack into
|
|
||||||
* @start: which segment of the sg_list to start at
|
|
||||||
* @limit: maximum segment to pack data to
|
|
||||||
* @data: data to pack into scatter/gather list
|
|
||||||
* @count: amount of data to pack into the scatter/gather list
|
|
||||||
*
|
|
||||||
* sg_lists have multiple segments of various sizes. This will pack
|
|
||||||
* arbitrary data into an existing scatter gather list, segmenting the
|
|
||||||
* data as necessary within constraints.
|
|
||||||
*
|
|
||||||
*/
|
|
||||||
|
|
||||||
static int pack_sg_list(struct scatterlist *sg, int start,
|
|
||||||
int limit, char *data, int count)
|
|
||||||
{
|
|
||||||
int s;
|
|
||||||
int index = start;
|
|
||||||
|
|
||||||
while (count) {
|
|
||||||
s = rest_of_page(data);
|
|
||||||
if (s > count)
|
|
||||||
s = count;
|
|
||||||
BUG_ON(index > limit);
|
|
||||||
/* Make sure we don't terminate early. */
|
|
||||||
sg_unmark_end(&sg[index]);
|
|
||||||
sg_set_buf(&sg[index++], data, s);
|
|
||||||
count -= s;
|
|
||||||
data += s;
|
|
||||||
}
|
|
||||||
if (index-start)
|
|
||||||
sg_mark_end(&sg[index - 1]);
|
|
||||||
return index-start;
|
|
||||||
}
|
|
||||||
|
|
||||||
/* We don't currently allow canceling of virtio requests */
|
|
||||||
static int p9_virtio_cancel(struct p9_client *client, struct p9_req_t *req)
|
|
||||||
{
|
|
||||||
return 1;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* pack_sg_list_p - Just like pack_sg_list. Instead of taking a buffer,
|
|
||||||
* this takes a list of pages.
|
|
||||||
* @sg: scatter/gather list to pack into
|
|
||||||
* @start: which segment of the sg_list to start at
|
|
||||||
* @pdata: a list of pages to add into sg.
|
|
||||||
* @nr_pages: number of pages to pack into the scatter/gather list
|
|
||||||
* @data: data to pack into scatter/gather list
|
|
||||||
* @count: amount of data to pack into the scatter/gather list
|
|
||||||
*/
|
|
||||||
static int
|
|
||||||
pack_sg_list_p(struct scatterlist *sg, int start, int limit,
|
|
||||||
struct page **pdata, int nr_pages, char *data, int count)
|
|
||||||
{
|
|
||||||
int i = 0, s;
|
|
||||||
int data_off;
|
|
||||||
int index = start;
|
|
||||||
|
|
||||||
BUG_ON(nr_pages > (limit - start));
|
|
||||||
/*
|
|
||||||
* if the first page doesn't start at
|
|
||||||
* page boundary find the offset
|
|
||||||
*/
|
|
||||||
data_off = offset_in_page(data);
|
|
||||||
while (nr_pages) {
|
|
||||||
s = rest_of_page(data);
|
|
||||||
if (s > count)
|
|
||||||
s = count;
|
|
||||||
/* Make sure we don't terminate early. */
|
|
||||||
sg_unmark_end(&sg[index]);
|
|
||||||
sg_set_page(&sg[index++], pdata[i++], s, data_off);
|
|
||||||
data_off = 0;
|
|
||||||
data += s;
|
|
||||||
count -= s;
|
|
||||||
nr_pages--;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (index-start)
|
|
||||||
sg_mark_end(&sg[index - 1]);
|
|
||||||
return index - start;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* p9_virtio_request - issue a request
|
|
||||||
* @client: client instance issuing the request
|
|
||||||
* @req: request to be issued
|
|
||||||
*
|
|
||||||
*/
|
|
||||||
|
|
||||||
static int
|
|
||||||
p9_virtio_request(struct p9_client *client, struct p9_req_t *req)
|
|
||||||
{
|
|
||||||
int err;
|
|
||||||
int in, out, out_sgs, in_sgs;
|
|
||||||
unsigned long flags;
|
|
||||||
struct virtio_chan *chan = client->trans;
|
|
||||||
struct scatterlist *sgs[2];
|
|
||||||
|
|
||||||
p9_debug(P9_DEBUG_TRANS, "9p debug: virtio request\n");
|
|
||||||
|
|
||||||
req->status = REQ_STATUS_SENT;
|
|
||||||
req_retry:
|
|
||||||
spin_lock_irqsave(&chan->lock, flags);
|
|
||||||
|
|
||||||
out_sgs = in_sgs = 0;
|
|
||||||
/* Handle out VirtIO ring buffers */
|
|
||||||
out = pack_sg_list(chan->sg, 0,
|
|
||||||
VIRTQUEUE_NUM, req->tc->sdata, req->tc->size);
|
|
||||||
if (out)
|
|
||||||
sgs[out_sgs++] = chan->sg;
|
|
||||||
|
|
||||||
in = pack_sg_list(chan->sg, out,
|
|
||||||
VIRTQUEUE_NUM, req->rc->sdata, req->rc->capacity);
|
|
||||||
if (in)
|
|
||||||
sgs[out_sgs + in_sgs++] = chan->sg + out;
|
|
||||||
|
|
||||||
err = virtqueue_add_sgs(chan->vq, sgs, out_sgs, in_sgs, req->tc,
|
|
||||||
GFP_ATOMIC);
|
|
||||||
if (err < 0) {
|
|
||||||
if (err == -ENOSPC) {
|
|
||||||
chan->ring_bufs_avail = 0;
|
|
||||||
spin_unlock_irqrestore(&chan->lock, flags);
|
|
||||||
err = wait_event_interruptible(*chan->vc_wq,
|
|
||||||
chan->ring_bufs_avail);
|
|
||||||
if (err == -ERESTARTSYS)
|
|
||||||
return err;
|
|
||||||
|
|
||||||
p9_debug(P9_DEBUG_TRANS, "Retry virtio request\n");
|
|
||||||
goto req_retry;
|
|
||||||
} else {
|
|
||||||
spin_unlock_irqrestore(&chan->lock, flags);
|
|
||||||
p9_debug(P9_DEBUG_TRANS,
|
|
||||||
"virtio rpc add_sgs returned failure\n");
|
|
||||||
return -EIO;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
virtqueue_kick(chan->vq);
|
|
||||||
spin_unlock_irqrestore(&chan->lock, flags);
|
|
||||||
|
|
||||||
p9_debug(P9_DEBUG_TRANS, "virtio request kicked\n");
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
static int p9_get_mapped_pages(struct virtio_chan *chan,
|
|
||||||
struct page **pages, char *data,
|
|
||||||
int nr_pages, int write, int kern_buf)
|
|
||||||
{
|
|
||||||
int err;
|
|
||||||
if (!kern_buf) {
|
|
||||||
/*
|
|
||||||
* We allow only p9_max_pages pinned. We wait for the
|
|
||||||
* Other zc request to finish here
|
|
||||||
*/
|
|
||||||
if (atomic_read(&vp_pinned) >= chan->p9_max_pages) {
|
|
||||||
err = wait_event_interruptible(vp_wq,
|
|
||||||
(atomic_read(&vp_pinned) < chan->p9_max_pages));
|
|
||||||
if (err == -ERESTARTSYS)
|
|
||||||
return err;
|
|
||||||
}
|
|
||||||
err = p9_payload_gup(data, &nr_pages, pages, write);
|
|
||||||
if (err < 0)
|
|
||||||
return err;
|
|
||||||
atomic_add(nr_pages, &vp_pinned);
|
|
||||||
} else {
|
|
||||||
/* kernel buffer, no need to pin pages */
|
|
||||||
int s, index = 0;
|
|
||||||
int count = nr_pages;
|
|
||||||
while (nr_pages) {
|
|
||||||
s = rest_of_page(data);
|
|
||||||
if (is_vmalloc_addr(data))
|
|
||||||
pages[index++] = vmalloc_to_page(data);
|
|
||||||
else
|
|
||||||
pages[index++] = kmap_to_page(data);
|
|
||||||
data += s;
|
|
||||||
nr_pages--;
|
|
||||||
}
|
|
||||||
nr_pages = count;
|
|
||||||
}
|
|
||||||
return nr_pages;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* p9_virtio_zc_request - issue a zero copy request
|
|
||||||
* @client: client instance issuing the request
|
|
||||||
* @req: request to be issued
|
|
||||||
* @uidata: user bffer that should be ued for zero copy read
|
|
||||||
* @uodata: user buffer that shoud be user for zero copy write
|
|
||||||
* @inlen: read buffer size
|
|
||||||
* @olen: write buffer size
|
|
||||||
* @hdrlen: reader header size, This is the size of response protocol data
|
|
||||||
*
|
|
||||||
*/
|
|
||||||
static int
|
|
||||||
p9_virtio_zc_request(struct p9_client *client, struct p9_req_t *req,
|
|
||||||
char *uidata, char *uodata, int inlen,
|
|
||||||
int outlen, int in_hdr_len, int kern_buf)
|
|
||||||
{
|
|
||||||
int in, out, err, out_sgs, in_sgs;
|
|
||||||
unsigned long flags;
|
|
||||||
int in_nr_pages = 0, out_nr_pages = 0;
|
|
||||||
struct page **in_pages = NULL, **out_pages = NULL;
|
|
||||||
struct virtio_chan *chan = client->trans;
|
|
||||||
struct scatterlist *sgs[4];
|
|
||||||
|
|
||||||
p9_debug(P9_DEBUG_TRANS, "virtio request\n");
|
|
||||||
|
|
||||||
if (uodata) {
|
|
||||||
out_nr_pages = p9_nr_pages(uodata, outlen);
|
|
||||||
out_pages = kmalloc(sizeof(struct page *) * out_nr_pages,
|
|
||||||
GFP_NOFS);
|
|
||||||
if (!out_pages) {
|
|
||||||
err = -ENOMEM;
|
|
||||||
goto err_out;
|
|
||||||
}
|
|
||||||
out_nr_pages = p9_get_mapped_pages(chan, out_pages, uodata,
|
|
||||||
out_nr_pages, 0, kern_buf);
|
|
||||||
if (out_nr_pages < 0) {
|
|
||||||
err = out_nr_pages;
|
|
||||||
kfree(out_pages);
|
|
||||||
out_pages = NULL;
|
|
||||||
goto err_out;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if (uidata) {
|
|
||||||
in_nr_pages = p9_nr_pages(uidata, inlen);
|
|
||||||
in_pages = kmalloc(sizeof(struct page *) * in_nr_pages,
|
|
||||||
GFP_NOFS);
|
|
||||||
if (!in_pages) {
|
|
||||||
err = -ENOMEM;
|
|
||||||
goto err_out;
|
|
||||||
}
|
|
||||||
in_nr_pages = p9_get_mapped_pages(chan, in_pages, uidata,
|
|
||||||
in_nr_pages, 1, kern_buf);
|
|
||||||
if (in_nr_pages < 0) {
|
|
||||||
err = in_nr_pages;
|
|
||||||
kfree(in_pages);
|
|
||||||
in_pages = NULL;
|
|
||||||
goto err_out;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
req->status = REQ_STATUS_SENT;
|
|
||||||
req_retry_pinned:
|
|
||||||
spin_lock_irqsave(&chan->lock, flags);
|
|
||||||
|
|
||||||
out_sgs = in_sgs = 0;
|
|
||||||
|
|
||||||
/* out data */
|
|
||||||
out = pack_sg_list(chan->sg, 0,
|
|
||||||
VIRTQUEUE_NUM, req->tc->sdata, req->tc->size);
|
|
||||||
|
|
||||||
if (out)
|
|
||||||
sgs[out_sgs++] = chan->sg;
|
|
||||||
|
|
||||||
if (out_pages) {
|
|
||||||
sgs[out_sgs++] = chan->sg + out;
|
|
||||||
out += pack_sg_list_p(chan->sg, out, VIRTQUEUE_NUM,
|
|
||||||
out_pages, out_nr_pages, uodata, outlen);
|
|
||||||
}
|
|
||||||
|
|
||||||
/*
|
|
||||||
* Take care of in data
|
|
||||||
* For example TREAD have 11.
|
|
||||||
* 11 is the read/write header = PDU Header(7) + IO Size (4).
|
|
||||||
* Arrange in such a way that server places header in the
|
|
||||||
* alloced memory and payload onto the user buffer.
|
|
||||||
*/
|
|
||||||
in = pack_sg_list(chan->sg, out,
|
|
||||||
VIRTQUEUE_NUM, req->rc->sdata, in_hdr_len);
|
|
||||||
if (in)
|
|
||||||
sgs[out_sgs + in_sgs++] = chan->sg + out;
|
|
||||||
|
|
||||||
if (in_pages) {
|
|
||||||
sgs[out_sgs + in_sgs++] = chan->sg + out + in;
|
|
||||||
in += pack_sg_list_p(chan->sg, out + in, VIRTQUEUE_NUM,
|
|
||||||
in_pages, in_nr_pages, uidata, inlen);
|
|
||||||
}
|
|
||||||
|
|
||||||
BUG_ON(out_sgs + in_sgs > ARRAY_SIZE(sgs));
|
|
||||||
err = virtqueue_add_sgs(chan->vq, sgs, out_sgs, in_sgs, req->tc,
|
|
||||||
GFP_ATOMIC);
|
|
||||||
if (err < 0) {
|
|
||||||
if (err == -ENOSPC) {
|
|
||||||
chan->ring_bufs_avail = 0;
|
|
||||||
spin_unlock_irqrestore(&chan->lock, flags);
|
|
||||||
err = wait_event_interruptible(*chan->vc_wq,
|
|
||||||
chan->ring_bufs_avail);
|
|
||||||
if (err == -ERESTARTSYS)
|
|
||||||
goto err_out;
|
|
||||||
|
|
||||||
p9_debug(P9_DEBUG_TRANS, "Retry virtio request\n");
|
|
||||||
goto req_retry_pinned;
|
|
||||||
} else {
|
|
||||||
spin_unlock_irqrestore(&chan->lock, flags);
|
|
||||||
p9_debug(P9_DEBUG_TRANS,
|
|
||||||
"virtio rpc add_sgs returned failure\n");
|
|
||||||
err = -EIO;
|
|
||||||
goto err_out;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
virtqueue_kick(chan->vq);
|
|
||||||
spin_unlock_irqrestore(&chan->lock, flags);
|
|
||||||
p9_debug(P9_DEBUG_TRANS, "virtio request kicked\n");
|
|
||||||
err = wait_event_interruptible(*req->wq,
|
|
||||||
req->status >= REQ_STATUS_RCVD);
|
|
||||||
/*
|
|
||||||
* Non kernel buffers are pinned, unpin them
|
|
||||||
*/
|
|
||||||
err_out:
|
|
||||||
if (!kern_buf) {
|
|
||||||
if (in_pages) {
|
|
||||||
p9_release_pages(in_pages, in_nr_pages);
|
|
||||||
atomic_sub(in_nr_pages, &vp_pinned);
|
|
||||||
}
|
|
||||||
if (out_pages) {
|
|
||||||
p9_release_pages(out_pages, out_nr_pages);
|
|
||||||
atomic_sub(out_nr_pages, &vp_pinned);
|
|
||||||
}
|
|
||||||
/* wakeup anybody waiting for slots to pin pages */
|
|
||||||
wake_up(&vp_wq);
|
|
||||||
}
|
|
||||||
kfree(in_pages);
|
|
||||||
kfree(out_pages);
|
|
||||||
return err;
|
|
||||||
}
|
|
||||||
|
|
||||||
static ssize_t p9_mount_tag_show(struct device *dev,
|
|
||||||
struct device_attribute *attr, char *buf)
|
|
||||||
{
|
|
||||||
struct virtio_chan *chan;
|
|
||||||
struct virtio_device *vdev;
|
|
||||||
|
|
||||||
vdev = dev_to_virtio(dev);
|
|
||||||
chan = vdev->priv;
|
|
||||||
|
|
||||||
return snprintf(buf, chan->tag_len + 1, "%s", chan->tag);
|
|
||||||
}
|
|
||||||
|
|
||||||
static DEVICE_ATTR(mount_tag, 0444, p9_mount_tag_show, NULL);
|
|
||||||
|
|
||||||
/**
|
|
||||||
* p9_virtio_probe - probe for existence of 9P virtio channels
|
|
||||||
* @vdev: virtio device to probe
|
|
||||||
*
|
|
||||||
* This probes for existing virtio channels.
|
|
||||||
*
|
|
||||||
*/
|
|
||||||
|
|
||||||
static int p9_virtio_probe(struct virtio_device *vdev)
|
|
||||||
{
|
|
||||||
__u16 tag_len;
|
|
||||||
char *tag;
|
|
||||||
int err;
|
|
||||||
struct virtio_chan *chan;
|
|
||||||
|
|
||||||
chan = kmalloc(sizeof(struct virtio_chan), GFP_KERNEL);
|
|
||||||
if (!chan) {
|
|
||||||
pr_err("Failed to allocate virtio 9P channel\n");
|
|
||||||
err = -ENOMEM;
|
|
||||||
goto fail;
|
|
||||||
}
|
|
||||||
|
|
||||||
chan->vdev = vdev;
|
|
||||||
|
|
||||||
/* We expect one virtqueue, for requests. */
|
|
||||||
chan->vq = virtio_find_single_vq(vdev, req_done, "requests");
|
|
||||||
if (IS_ERR(chan->vq)) {
|
|
||||||
err = PTR_ERR(chan->vq);
|
|
||||||
goto out_free_vq;
|
|
||||||
}
|
|
||||||
chan->vq->vdev->priv = chan;
|
|
||||||
spin_lock_init(&chan->lock);
|
|
||||||
|
|
||||||
sg_init_table(chan->sg, VIRTQUEUE_NUM);
|
|
||||||
|
|
||||||
chan->inuse = false;
|
|
||||||
if (virtio_has_feature(vdev, VIRTIO_9P_MOUNT_TAG)) {
|
|
||||||
vdev->config->get(vdev,
|
|
||||||
offsetof(struct virtio_9p_config, tag_len),
|
|
||||||
&tag_len, sizeof(tag_len));
|
|
||||||
} else {
|
|
||||||
err = -EINVAL;
|
|
||||||
goto out_free_vq;
|
|
||||||
}
|
|
||||||
tag = kmalloc(tag_len, GFP_KERNEL);
|
|
||||||
if (!tag) {
|
|
||||||
err = -ENOMEM;
|
|
||||||
goto out_free_vq;
|
|
||||||
}
|
|
||||||
vdev->config->get(vdev, offsetof(struct virtio_9p_config, tag),
|
|
||||||
tag, tag_len);
|
|
||||||
chan->tag = tag;
|
|
||||||
chan->tag_len = tag_len;
|
|
||||||
err = sysfs_create_file(&(vdev->dev.kobj), &dev_attr_mount_tag.attr);
|
|
||||||
if (err) {
|
|
||||||
goto out_free_tag;
|
|
||||||
}
|
|
||||||
chan->vc_wq = kmalloc(sizeof(wait_queue_head_t), GFP_KERNEL);
|
|
||||||
if (!chan->vc_wq) {
|
|
||||||
err = -ENOMEM;
|
|
||||||
goto out_free_tag;
|
|
||||||
}
|
|
||||||
init_waitqueue_head(chan->vc_wq);
|
|
||||||
chan->ring_bufs_avail = 1;
|
|
||||||
/* Ceiling limit to avoid denial of service attacks */
|
|
||||||
chan->p9_max_pages = nr_free_buffer_pages()/4;
|
|
||||||
|
|
||||||
mutex_lock(&virtio_9p_lock);
|
|
||||||
list_add_tail(&chan->chan_list, &virtio_chan_list);
|
|
||||||
mutex_unlock(&virtio_9p_lock);
|
|
||||||
|
|
||||||
/* Let udev rules use the new mount_tag attribute. */
|
|
||||||
kobject_uevent(&(vdev->dev.kobj), KOBJ_CHANGE);
|
|
||||||
|
|
||||||
return 0;
|
|
||||||
|
|
||||||
out_free_tag:
|
|
||||||
kfree(tag);
|
|
||||||
out_free_vq:
|
|
||||||
vdev->config->del_vqs(vdev);
|
|
||||||
kfree(chan);
|
|
||||||
fail:
|
|
||||||
return err;
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
/**
|
|
||||||
* p9_virtio_create - allocate a new virtio channel
|
|
||||||
* @client: client instance invoking this transport
|
|
||||||
* @devname: string identifying the channel to connect to (unused)
|
|
||||||
* @args: args passed from sys_mount() for per-transport options (unused)
|
|
||||||
*
|
|
||||||
* This sets up a transport channel for 9p communication. Right now
|
|
||||||
* we only match the first available channel, but eventually we couldlook up
|
|
||||||
* alternate channels by matching devname versus a virtio_config entry.
|
|
||||||
* We use a simple reference count mechanism to ensure that only a single
|
|
||||||
* mount has a channel open at a time.
|
|
||||||
*
|
|
||||||
*/
|
|
||||||
|
|
||||||
static int
|
|
||||||
p9_virtio_create(struct p9_client *client, const char *devname, char *args)
|
|
||||||
{
|
|
||||||
struct virtio_chan *chan;
|
|
||||||
int ret = -ENOENT;
|
|
||||||
int found = 0;
|
|
||||||
|
|
||||||
mutex_lock(&virtio_9p_lock);
|
|
||||||
list_for_each_entry(chan, &virtio_chan_list, chan_list) {
|
|
||||||
if (!strncmp(devname, chan->tag, chan->tag_len) &&
|
|
||||||
strlen(devname) == chan->tag_len) {
|
|
||||||
if (!chan->inuse) {
|
|
||||||
chan->inuse = true;
|
|
||||||
found = 1;
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
ret = -EBUSY;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
mutex_unlock(&virtio_9p_lock);
|
|
||||||
|
|
||||||
if (!found) {
|
|
||||||
pr_err("no channels available\n");
|
|
||||||
return ret;
|
|
||||||
}
|
|
||||||
|
|
||||||
client->trans = (void *)chan;
|
|
||||||
client->status = Connected;
|
|
||||||
chan->client = client;
|
|
||||||
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* p9_virtio_remove - clean up resources associated with a virtio device
|
|
||||||
* @vdev: virtio device to remove
|
|
||||||
*
|
|
||||||
*/
|
|
||||||
|
|
||||||
static void p9_virtio_remove(struct virtio_device *vdev)
|
|
||||||
{
|
|
||||||
struct virtio_chan *chan = vdev->priv;
|
|
||||||
|
|
||||||
if (chan->inuse)
|
|
||||||
p9_virtio_close(chan->client);
|
|
||||||
vdev->config->del_vqs(vdev);
|
|
||||||
|
|
||||||
mutex_lock(&virtio_9p_lock);
|
|
||||||
list_del(&chan->chan_list);
|
|
||||||
mutex_unlock(&virtio_9p_lock);
|
|
||||||
sysfs_remove_file(&(vdev->dev.kobj), &dev_attr_mount_tag.attr);
|
|
||||||
kobject_uevent(&(vdev->dev.kobj), KOBJ_CHANGE);
|
|
||||||
kfree(chan->tag);
|
|
||||||
kfree(chan->vc_wq);
|
|
||||||
kfree(chan);
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
static struct virtio_device_id id_table[] = {
|
|
||||||
{ VIRTIO_ID_9P, VIRTIO_DEV_ANY_ID },
|
|
||||||
{ 0 },
|
|
||||||
};
|
|
||||||
|
|
||||||
static unsigned int features[] = {
|
|
||||||
VIRTIO_9P_MOUNT_TAG,
|
|
||||||
};
|
|
||||||
|
|
||||||
/* The standard "struct lguest_driver": */
|
|
||||||
static struct virtio_driver p9_virtio_drv = {
|
|
||||||
.feature_table = features,
|
|
||||||
.feature_table_size = ARRAY_SIZE(features),
|
|
||||||
.driver.name = KBUILD_MODNAME,
|
|
||||||
.driver.owner = THIS_MODULE,
|
|
||||||
.id_table = id_table,
|
|
||||||
.probe = p9_virtio_probe,
|
|
||||||
.remove = p9_virtio_remove,
|
|
||||||
};
|
|
||||||
|
|
||||||
static struct p9_trans_module p9_virtio_trans = {
|
|
||||||
.name = "virtio",
|
|
||||||
.create = p9_virtio_create,
|
|
||||||
.close = p9_virtio_close,
|
|
||||||
.request = p9_virtio_request,
|
|
||||||
.zc_request = p9_virtio_zc_request,
|
|
||||||
.cancel = p9_virtio_cancel,
|
|
||||||
/*
|
|
||||||
* We leave one entry for input and one entry for response
|
|
||||||
* headers. We also skip one more entry to accomodate, address
|
|
||||||
* that are not at page boundary, that can result in an extra
|
|
||||||
* page in zero copy.
|
|
||||||
*/
|
|
||||||
.maxsize = PAGE_SIZE * (VIRTQUEUE_NUM - 3),
|
|
||||||
.def = 0,
|
|
||||||
.owner = THIS_MODULE,
|
|
||||||
};
|
|
||||||
|
|
||||||
/* The standard init function */
|
|
||||||
static int __init p9_virtio_init(void)
|
|
||||||
{
|
|
||||||
INIT_LIST_HEAD(&virtio_chan_list);
|
|
||||||
|
|
||||||
v9fs_register_trans(&p9_virtio_trans);
|
|
||||||
return register_virtio_driver(&p9_virtio_drv);
|
|
||||||
}
|
|
||||||
|
|
||||||
static void __exit p9_virtio_cleanup(void)
|
|
||||||
{
|
|
||||||
unregister_virtio_driver(&p9_virtio_drv);
|
|
||||||
v9fs_unregister_trans(&p9_virtio_trans);
|
|
||||||
}
|
|
||||||
|
|
||||||
module_init(p9_virtio_init);
|
|
||||||
module_exit(p9_virtio_cleanup);
|
|
||||||
|
|
||||||
MODULE_DEVICE_TABLE(virtio, id_table);
|
|
||||||
MODULE_AUTHOR("Eric Van Hensbergen <ericvh@gmail.com>");
|
|
||||||
MODULE_DESCRIPTION("Virtio 9p Transport");
|
|
||||||
MODULE_LICENSE("GPL");
|
|
@ -1,141 +0,0 @@
|
|||||||
/*
|
|
||||||
* net/9p/util.c
|
|
||||||
*
|
|
||||||
* This file contains some helper functions
|
|
||||||
*
|
|
||||||
* Copyright (C) 2007 by Latchesar Ionkov <lucho@ionkov.net>
|
|
||||||
* Copyright (C) 2004 by Eric Van Hensbergen <ericvh@gmail.com>
|
|
||||||
* Copyright (C) 2002 by Ron Minnich <rminnich@lanl.gov>
|
|
||||||
*
|
|
||||||
* This program is free software; you can redistribute it and/or modify
|
|
||||||
* it under the terms of the GNU General Public License version 2
|
|
||||||
* as published by the Free Software Foundation.
|
|
||||||
*
|
|
||||||
* This program is distributed in the hope that it will be useful,
|
|
||||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
||||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
||||||
* GNU General Public License for more details.
|
|
||||||
*
|
|
||||||
* You should have received a copy of the GNU General Public License
|
|
||||||
* along with this program; if not, write to:
|
|
||||||
* Free Software Foundation
|
|
||||||
* 51 Franklin Street, Fifth Floor
|
|
||||||
* Boston, MA 02111-1301 USA
|
|
||||||
*
|
|
||||||
*/
|
|
||||||
|
|
||||||
#include <linux/module.h>
|
|
||||||
#include <linux/errno.h>
|
|
||||||
#include <linux/fs.h>
|
|
||||||
#include <linux/sched.h>
|
|
||||||
#include <linux/parser.h>
|
|
||||||
#include <linux/idr.h>
|
|
||||||
#include <linux/slab.h>
|
|
||||||
#include <net/9p/9p.h>
|
|
||||||
|
|
||||||
/**
|
|
||||||
* struct p9_idpool - per-connection accounting for tag idpool
|
|
||||||
* @lock: protects the pool
|
|
||||||
* @pool: idr to allocate tag id from
|
|
||||||
*
|
|
||||||
*/
|
|
||||||
|
|
||||||
struct p9_idpool {
|
|
||||||
spinlock_t lock;
|
|
||||||
struct idr pool;
|
|
||||||
};
|
|
||||||
|
|
||||||
/**
|
|
||||||
* p9_idpool_create - create a new per-connection id pool
|
|
||||||
*
|
|
||||||
*/
|
|
||||||
|
|
||||||
struct p9_idpool *p9_idpool_create(void)
|
|
||||||
{
|
|
||||||
struct p9_idpool *p;
|
|
||||||
|
|
||||||
p = kmalloc(sizeof(struct p9_idpool), GFP_KERNEL);
|
|
||||||
if (!p)
|
|
||||||
return ERR_PTR(-ENOMEM);
|
|
||||||
|
|
||||||
spin_lock_init(&p->lock);
|
|
||||||
idr_init(&p->pool);
|
|
||||||
|
|
||||||
return p;
|
|
||||||
}
|
|
||||||
EXPORT_SYMBOL(p9_idpool_create);
|
|
||||||
|
|
||||||
/**
|
|
||||||
* p9_idpool_destroy - create a new per-connection id pool
|
|
||||||
* @p: idpool to destroy
|
|
||||||
*/
|
|
||||||
|
|
||||||
void p9_idpool_destroy(struct p9_idpool *p)
|
|
||||||
{
|
|
||||||
idr_destroy(&p->pool);
|
|
||||||
kfree(p);
|
|
||||||
}
|
|
||||||
EXPORT_SYMBOL(p9_idpool_destroy);
|
|
||||||
|
|
||||||
/**
|
|
||||||
* p9_idpool_get - allocate numeric id from pool
|
|
||||||
* @p: pool to allocate from
|
|
||||||
*
|
|
||||||
* Bugs: This seems to be an awful generic function, should it be in idr.c with
|
|
||||||
* the lock included in struct idr?
|
|
||||||
*/
|
|
||||||
|
|
||||||
int p9_idpool_get(struct p9_idpool *p)
|
|
||||||
{
|
|
||||||
int i;
|
|
||||||
unsigned long flags;
|
|
||||||
|
|
||||||
idr_preload(GFP_NOFS);
|
|
||||||
spin_lock_irqsave(&p->lock, flags);
|
|
||||||
|
|
||||||
/* no need to store exactly p, we just need something non-null */
|
|
||||||
i = idr_alloc(&p->pool, p, 0, 0, GFP_NOWAIT);
|
|
||||||
|
|
||||||
spin_unlock_irqrestore(&p->lock, flags);
|
|
||||||
idr_preload_end();
|
|
||||||
if (i < 0)
|
|
||||||
return -1;
|
|
||||||
|
|
||||||
p9_debug(P9_DEBUG_MUX, " id %d pool %p\n", i, p);
|
|
||||||
return i;
|
|
||||||
}
|
|
||||||
EXPORT_SYMBOL(p9_idpool_get);
|
|
||||||
|
|
||||||
/**
|
|
||||||
* p9_idpool_put - release numeric id from pool
|
|
||||||
* @id: numeric id which is being released
|
|
||||||
* @p: pool to release id into
|
|
||||||
*
|
|
||||||
* Bugs: This seems to be an awful generic function, should it be in idr.c with
|
|
||||||
* the lock included in struct idr?
|
|
||||||
*/
|
|
||||||
|
|
||||||
void p9_idpool_put(int id, struct p9_idpool *p)
|
|
||||||
{
|
|
||||||
unsigned long flags;
|
|
||||||
|
|
||||||
p9_debug(P9_DEBUG_MUX, " id %d pool %p\n", id, p);
|
|
||||||
|
|
||||||
spin_lock_irqsave(&p->lock, flags);
|
|
||||||
idr_remove(&p->pool, id);
|
|
||||||
spin_unlock_irqrestore(&p->lock, flags);
|
|
||||||
}
|
|
||||||
EXPORT_SYMBOL(p9_idpool_put);
|
|
||||||
|
|
||||||
/**
|
|
||||||
* p9_idpool_check - check if the specified id is available
|
|
||||||
* @id: id to check
|
|
||||||
* @p: pool to check
|
|
||||||
*/
|
|
||||||
|
|
||||||
int p9_idpool_check(int id, struct p9_idpool *p)
|
|
||||||
{
|
|
||||||
return idr_find(&p->pool, id) != NULL;
|
|
||||||
}
|
|
||||||
EXPORT_SYMBOL(p9_idpool_check);
|
|
||||||
|
|
@ -1,677 +0,0 @@
|
|||||||
/*
|
|
||||||
* linux/fs/9p/v9fs.c
|
|
||||||
*
|
|
||||||
* This file contains functions assisting in mapping VFS to 9P2000
|
|
||||||
*
|
|
||||||
* Copyright (C) 2004-2008 by Eric Van Hensbergen <ericvh@gmail.com>
|
|
||||||
* Copyright (C) 2002 by Ron Minnich <rminnich@lanl.gov>
|
|
||||||
*
|
|
||||||
* This program is free software; you can redistribute it and/or modify
|
|
||||||
* it under the terms of the GNU General Public License version 2
|
|
||||||
* as published by the Free Software Foundation.
|
|
||||||
*
|
|
||||||
* This program is distributed in the hope that it will be useful,
|
|
||||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
||||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
||||||
* GNU General Public License for more details.
|
|
||||||
*
|
|
||||||
* You should have received a copy of the GNU General Public License
|
|
||||||
* along with this program; if not, write to:
|
|
||||||
* Free Software Foundation
|
|
||||||
* 51 Franklin Street, Fifth Floor
|
|
||||||
* Boston, MA 02111-1301 USA
|
|
||||||
*
|
|
||||||
*/
|
|
||||||
|
|
||||||
#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
|
|
||||||
|
|
||||||
#include <linux/module.h>
|
|
||||||
#include <linux/errno.h>
|
|
||||||
#include <linux/fs.h>
|
|
||||||
#include <linux/sched.h>
|
|
||||||
#include <linux/parser.h>
|
|
||||||
#include <linux/idr.h>
|
|
||||||
#include <linux/slab.h>
|
|
||||||
#include <net/9p/9p.h>
|
|
||||||
#include <net/9p/client.h>
|
|
||||||
#include <net/9p/transport.h>
|
|
||||||
#include "v9fs.h"
|
|
||||||
#include "v9fs_vfs.h"
|
|
||||||
#include "cache.h"
|
|
||||||
|
|
||||||
static DEFINE_SPINLOCK(v9fs_sessionlist_lock);
|
|
||||||
static LIST_HEAD(v9fs_sessionlist);
|
|
||||||
struct kmem_cache *v9fs_inode_cache;
|
|
||||||
|
|
||||||
/*
|
|
||||||
* Option Parsing (code inspired by NFS code)
|
|
||||||
* NOTE: each transport will parse its own options
|
|
||||||
*/
|
|
||||||
|
|
||||||
enum {
|
|
||||||
/* Options that take integer arguments */
|
|
||||||
Opt_debug, Opt_dfltuid, Opt_dfltgid, Opt_afid,
|
|
||||||
/* String options */
|
|
||||||
Opt_uname, Opt_remotename, Opt_trans, Opt_cache, Opt_cachetag,
|
|
||||||
/* Options that take no arguments */
|
|
||||||
Opt_nodevmap,
|
|
||||||
/* Cache options */
|
|
||||||
Opt_cache_loose, Opt_fscache,
|
|
||||||
/* Access options */
|
|
||||||
Opt_access, Opt_posixacl,
|
|
||||||
/* Error token */
|
|
||||||
Opt_err
|
|
||||||
};
|
|
||||||
|
|
||||||
static const match_table_t tokens = {
|
|
||||||
{Opt_debug, "debug=%x"},
|
|
||||||
{Opt_dfltuid, "dfltuid=%u"},
|
|
||||||
{Opt_dfltgid, "dfltgid=%u"},
|
|
||||||
{Opt_afid, "afid=%u"},
|
|
||||||
{Opt_uname, "uname=%s"},
|
|
||||||
{Opt_remotename, "aname=%s"},
|
|
||||||
{Opt_nodevmap, "nodevmap"},
|
|
||||||
{Opt_cache, "cache=%s"},
|
|
||||||
{Opt_cache_loose, "loose"},
|
|
||||||
{Opt_fscache, "fscache"},
|
|
||||||
{Opt_cachetag, "cachetag=%s"},
|
|
||||||
{Opt_access, "access=%s"},
|
|
||||||
{Opt_posixacl, "posixacl"},
|
|
||||||
{Opt_err, NULL}
|
|
||||||
};
|
|
||||||
|
|
||||||
/* Interpret mount options for cache mode */
|
|
||||||
static int get_cache_mode(char *s)
|
|
||||||
{
|
|
||||||
int version = -EINVAL;
|
|
||||||
|
|
||||||
if (!strcmp(s, "loose")) {
|
|
||||||
version = CACHE_LOOSE;
|
|
||||||
p9_debug(P9_DEBUG_9P, "Cache mode: loose\n");
|
|
||||||
} else if (!strcmp(s, "fscache")) {
|
|
||||||
version = CACHE_FSCACHE;
|
|
||||||
p9_debug(P9_DEBUG_9P, "Cache mode: fscache\n");
|
|
||||||
} else if (!strcmp(s, "none")) {
|
|
||||||
version = CACHE_NONE;
|
|
||||||
p9_debug(P9_DEBUG_9P, "Cache mode: none\n");
|
|
||||||
} else
|
|
||||||
pr_info("Unknown Cache mode %s\n", s);
|
|
||||||
return version;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* v9fs_parse_options - parse mount options into session structure
|
|
||||||
* @v9ses: existing v9fs session information
|
|
||||||
*
|
|
||||||
* Return 0 upon success, -ERRNO upon failure.
|
|
||||||
*/
|
|
||||||
|
|
||||||
static int v9fs_parse_options(struct v9fs_session_info *v9ses, char *opts)
|
|
||||||
{
|
|
||||||
char *options, *tmp_options;
|
|
||||||
substring_t args[MAX_OPT_ARGS];
|
|
||||||
char *p;
|
|
||||||
int option = 0;
|
|
||||||
char *s, *e;
|
|
||||||
int ret = 0;
|
|
||||||
|
|
||||||
/* setup defaults */
|
|
||||||
v9ses->afid = ~0;
|
|
||||||
v9ses->debug = 0;
|
|
||||||
v9ses->cache = CACHE_NONE;
|
|
||||||
#ifdef CONFIG_9P_FSCACHE
|
|
||||||
v9ses->cachetag = NULL;
|
|
||||||
#endif
|
|
||||||
|
|
||||||
if (!opts)
|
|
||||||
return 0;
|
|
||||||
|
|
||||||
tmp_options = kstrdup(opts, GFP_KERNEL);
|
|
||||||
if (!tmp_options) {
|
|
||||||
ret = -ENOMEM;
|
|
||||||
goto fail_option_alloc;
|
|
||||||
}
|
|
||||||
options = tmp_options;
|
|
||||||
|
|
||||||
while ((p = strsep(&options, ",")) != NULL) {
|
|
||||||
int token, r;
|
|
||||||
if (!*p)
|
|
||||||
continue;
|
|
||||||
token = match_token(p, tokens, args);
|
|
||||||
switch (token) {
|
|
||||||
case Opt_debug:
|
|
||||||
r = match_int(&args[0], &option);
|
|
||||||
if (r < 0) {
|
|
||||||
p9_debug(P9_DEBUG_ERROR,
|
|
||||||
"integer field, but no integer?\n");
|
|
||||||
ret = r;
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
v9ses->debug = option;
|
|
||||||
#ifdef CONFIG_NET_9P_DEBUG
|
|
||||||
p9_debug_level = option;
|
|
||||||
#endif
|
|
||||||
break;
|
|
||||||
|
|
||||||
case Opt_dfltuid:
|
|
||||||
r = match_int(&args[0], &option);
|
|
||||||
if (r < 0) {
|
|
||||||
p9_debug(P9_DEBUG_ERROR,
|
|
||||||
"integer field, but no integer?\n");
|
|
||||||
ret = r;
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
v9ses->dfltuid = make_kuid(current_user_ns(), option);
|
|
||||||
if (!uid_valid(v9ses->dfltuid)) {
|
|
||||||
p9_debug(P9_DEBUG_ERROR,
|
|
||||||
"uid field, but not a uid?\n");
|
|
||||||
ret = -EINVAL;
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
break;
|
|
||||||
case Opt_dfltgid:
|
|
||||||
r = match_int(&args[0], &option);
|
|
||||||
if (r < 0) {
|
|
||||||
p9_debug(P9_DEBUG_ERROR,
|
|
||||||
"integer field, but no integer?\n");
|
|
||||||
ret = r;
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
v9ses->dfltgid = make_kgid(current_user_ns(), option);
|
|
||||||
if (!gid_valid(v9ses->dfltgid)) {
|
|
||||||
p9_debug(P9_DEBUG_ERROR,
|
|
||||||
"gid field, but not a gid?\n");
|
|
||||||
ret = -EINVAL;
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
break;
|
|
||||||
case Opt_afid:
|
|
||||||
r = match_int(&args[0], &option);
|
|
||||||
if (r < 0) {
|
|
||||||
p9_debug(P9_DEBUG_ERROR,
|
|
||||||
"integer field, but no integer?\n");
|
|
||||||
ret = r;
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
v9ses->afid = option;
|
|
||||||
break;
|
|
||||||
case Opt_uname:
|
|
||||||
kfree(v9ses->uname);
|
|
||||||
v9ses->uname = match_strdup(&args[0]);
|
|
||||||
if (!v9ses->uname) {
|
|
||||||
ret = -ENOMEM;
|
|
||||||
goto free_and_return;
|
|
||||||
}
|
|
||||||
break;
|
|
||||||
case Opt_remotename:
|
|
||||||
kfree(v9ses->aname);
|
|
||||||
v9ses->aname = match_strdup(&args[0]);
|
|
||||||
if (!v9ses->aname) {
|
|
||||||
ret = -ENOMEM;
|
|
||||||
goto free_and_return;
|
|
||||||
}
|
|
||||||
break;
|
|
||||||
case Opt_nodevmap:
|
|
||||||
v9ses->nodev = 1;
|
|
||||||
break;
|
|
||||||
case Opt_cache_loose:
|
|
||||||
v9ses->cache = CACHE_LOOSE;
|
|
||||||
break;
|
|
||||||
case Opt_fscache:
|
|
||||||
v9ses->cache = CACHE_FSCACHE;
|
|
||||||
break;
|
|
||||||
case Opt_cachetag:
|
|
||||||
#ifdef CONFIG_9P_FSCACHE
|
|
||||||
v9ses->cachetag = match_strdup(&args[0]);
|
|
||||||
#endif
|
|
||||||
break;
|
|
||||||
case Opt_cache:
|
|
||||||
s = match_strdup(&args[0]);
|
|
||||||
if (!s) {
|
|
||||||
ret = -ENOMEM;
|
|
||||||
p9_debug(P9_DEBUG_ERROR,
|
|
||||||
"problem allocating copy of cache arg\n");
|
|
||||||
goto free_and_return;
|
|
||||||
}
|
|
||||||
ret = get_cache_mode(s);
|
|
||||||
if (ret == -EINVAL) {
|
|
||||||
kfree(s);
|
|
||||||
goto free_and_return;
|
|
||||||
}
|
|
||||||
|
|
||||||
v9ses->cache = ret;
|
|
||||||
kfree(s);
|
|
||||||
break;
|
|
||||||
|
|
||||||
case Opt_access:
|
|
||||||
s = match_strdup(&args[0]);
|
|
||||||
if (!s) {
|
|
||||||
ret = -ENOMEM;
|
|
||||||
p9_debug(P9_DEBUG_ERROR,
|
|
||||||
"problem allocating copy of access arg\n");
|
|
||||||
goto free_and_return;
|
|
||||||
}
|
|
||||||
|
|
||||||
v9ses->flags &= ~V9FS_ACCESS_MASK;
|
|
||||||
if (strcmp(s, "user") == 0)
|
|
||||||
v9ses->flags |= V9FS_ACCESS_USER;
|
|
||||||
else if (strcmp(s, "any") == 0)
|
|
||||||
v9ses->flags |= V9FS_ACCESS_ANY;
|
|
||||||
else if (strcmp(s, "client") == 0) {
|
|
||||||
v9ses->flags |= V9FS_ACCESS_CLIENT;
|
|
||||||
} else {
|
|
||||||
uid_t uid;
|
|
||||||
v9ses->flags |= V9FS_ACCESS_SINGLE;
|
|
||||||
uid = simple_strtoul(s, &e, 10);
|
|
||||||
if (*e != '\0') {
|
|
||||||
ret = -EINVAL;
|
|
||||||
pr_info("Unknown access argument %s\n",
|
|
||||||
s);
|
|
||||||
kfree(s);
|
|
||||||
goto free_and_return;
|
|
||||||
}
|
|
||||||
v9ses->uid = make_kuid(current_user_ns(), uid);
|
|
||||||
if (!uid_valid(v9ses->uid)) {
|
|
||||||
ret = -EINVAL;
|
|
||||||
pr_info("Uknown uid %s\n", s);
|
|
||||||
kfree(s);
|
|
||||||
goto free_and_return;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
kfree(s);
|
|
||||||
break;
|
|
||||||
|
|
||||||
case Opt_posixacl:
|
|
||||||
#ifdef CONFIG_9P_FS_POSIX_ACL
|
|
||||||
v9ses->flags |= V9FS_POSIX_ACL;
|
|
||||||
#else
|
|
||||||
p9_debug(P9_DEBUG_ERROR,
|
|
||||||
"Not defined CONFIG_9P_FS_POSIX_ACL. Ignoring posixacl option\n");
|
|
||||||
#endif
|
|
||||||
break;
|
|
||||||
|
|
||||||
default:
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
free_and_return:
|
|
||||||
kfree(tmp_options);
|
|
||||||
fail_option_alloc:
|
|
||||||
return ret;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* v9fs_session_init - initialize session
|
|
||||||
* @v9ses: session information structure
|
|
||||||
* @dev_name: device being mounted
|
|
||||||
* @data: options
|
|
||||||
*
|
|
||||||
*/
|
|
||||||
|
|
||||||
struct p9_fid *v9fs_session_init(struct v9fs_session_info *v9ses,
|
|
||||||
const char *dev_name, char *data)
|
|
||||||
{
|
|
||||||
int retval = -EINVAL;
|
|
||||||
struct p9_fid *fid;
|
|
||||||
int rc;
|
|
||||||
|
|
||||||
v9ses->uname = kstrdup(V9FS_DEFUSER, GFP_KERNEL);
|
|
||||||
if (!v9ses->uname)
|
|
||||||
return ERR_PTR(-ENOMEM);
|
|
||||||
|
|
||||||
v9ses->aname = kstrdup(V9FS_DEFANAME, GFP_KERNEL);
|
|
||||||
if (!v9ses->aname) {
|
|
||||||
kfree(v9ses->uname);
|
|
||||||
return ERR_PTR(-ENOMEM);
|
|
||||||
}
|
|
||||||
init_rwsem(&v9ses->rename_sem);
|
|
||||||
|
|
||||||
rc = bdi_setup_and_register(&v9ses->bdi, "9p", BDI_CAP_MAP_COPY);
|
|
||||||
if (rc) {
|
|
||||||
kfree(v9ses->aname);
|
|
||||||
kfree(v9ses->uname);
|
|
||||||
return ERR_PTR(rc);
|
|
||||||
}
|
|
||||||
|
|
||||||
spin_lock(&v9fs_sessionlist_lock);
|
|
||||||
list_add(&v9ses->slist, &v9fs_sessionlist);
|
|
||||||
spin_unlock(&v9fs_sessionlist_lock);
|
|
||||||
|
|
||||||
v9ses->uid = INVALID_UID;
|
|
||||||
v9ses->dfltuid = V9FS_DEFUID;
|
|
||||||
v9ses->dfltgid = V9FS_DEFGID;
|
|
||||||
|
|
||||||
v9ses->clnt = p9_client_create(dev_name, data);
|
|
||||||
if (IS_ERR(v9ses->clnt)) {
|
|
||||||
retval = PTR_ERR(v9ses->clnt);
|
|
||||||
v9ses->clnt = NULL;
|
|
||||||
p9_debug(P9_DEBUG_ERROR, "problem initializing 9p client\n");
|
|
||||||
goto error;
|
|
||||||
}
|
|
||||||
|
|
||||||
v9ses->flags = V9FS_ACCESS_USER;
|
|
||||||
|
|
||||||
if (p9_is_proto_dotl(v9ses->clnt)) {
|
|
||||||
v9ses->flags = V9FS_ACCESS_CLIENT;
|
|
||||||
v9ses->flags |= V9FS_PROTO_2000L;
|
|
||||||
} else if (p9_is_proto_dotu(v9ses->clnt)) {
|
|
||||||
v9ses->flags |= V9FS_PROTO_2000U;
|
|
||||||
}
|
|
||||||
|
|
||||||
rc = v9fs_parse_options(v9ses, data);
|
|
||||||
if (rc < 0) {
|
|
||||||
retval = rc;
|
|
||||||
goto error;
|
|
||||||
}
|
|
||||||
|
|
||||||
v9ses->maxdata = v9ses->clnt->msize - P9_IOHDRSZ;
|
|
||||||
|
|
||||||
if (!v9fs_proto_dotl(v9ses) &&
|
|
||||||
((v9ses->flags & V9FS_ACCESS_MASK) == V9FS_ACCESS_CLIENT)) {
|
|
||||||
/*
|
|
||||||
* We support ACCESS_CLIENT only for dotl.
|
|
||||||
* Fall back to ACCESS_USER
|
|
||||||
*/
|
|
||||||
v9ses->flags &= ~V9FS_ACCESS_MASK;
|
|
||||||
v9ses->flags |= V9FS_ACCESS_USER;
|
|
||||||
}
|
|
||||||
/*FIXME !! */
|
|
||||||
/* for legacy mode, fall back to V9FS_ACCESS_ANY */
|
|
||||||
if (!(v9fs_proto_dotu(v9ses) || v9fs_proto_dotl(v9ses)) &&
|
|
||||||
((v9ses->flags&V9FS_ACCESS_MASK) == V9FS_ACCESS_USER)) {
|
|
||||||
|
|
||||||
v9ses->flags &= ~V9FS_ACCESS_MASK;
|
|
||||||
v9ses->flags |= V9FS_ACCESS_ANY;
|
|
||||||
v9ses->uid = INVALID_UID;
|
|
||||||
}
|
|
||||||
if (!v9fs_proto_dotl(v9ses) ||
|
|
||||||
!((v9ses->flags & V9FS_ACCESS_MASK) == V9FS_ACCESS_CLIENT)) {
|
|
||||||
/*
|
|
||||||
* We support ACL checks on clinet only if the protocol is
|
|
||||||
* 9P2000.L and access is V9FS_ACCESS_CLIENT.
|
|
||||||
*/
|
|
||||||
v9ses->flags &= ~V9FS_ACL_MASK;
|
|
||||||
}
|
|
||||||
|
|
||||||
fid = p9_client_attach(v9ses->clnt, NULL, v9ses->uname, INVALID_UID,
|
|
||||||
v9ses->aname);
|
|
||||||
if (IS_ERR(fid)) {
|
|
||||||
retval = PTR_ERR(fid);
|
|
||||||
fid = NULL;
|
|
||||||
p9_debug(P9_DEBUG_ERROR, "cannot attach\n");
|
|
||||||
goto error;
|
|
||||||
}
|
|
||||||
|
|
||||||
if ((v9ses->flags & V9FS_ACCESS_MASK) == V9FS_ACCESS_SINGLE)
|
|
||||||
fid->uid = v9ses->uid;
|
|
||||||
else
|
|
||||||
fid->uid = INVALID_UID;
|
|
||||||
|
|
||||||
#ifdef CONFIG_9P_FSCACHE
|
|
||||||
/* register the session for caching */
|
|
||||||
v9fs_cache_session_get_cookie(v9ses);
|
|
||||||
#endif
|
|
||||||
|
|
||||||
return fid;
|
|
||||||
|
|
||||||
error:
|
|
||||||
bdi_destroy(&v9ses->bdi);
|
|
||||||
return ERR_PTR(retval);
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* v9fs_session_close - shutdown a session
|
|
||||||
* @v9ses: session information structure
|
|
||||||
*
|
|
||||||
*/
|
|
||||||
|
|
||||||
void v9fs_session_close(struct v9fs_session_info *v9ses)
|
|
||||||
{
|
|
||||||
if (v9ses->clnt) {
|
|
||||||
p9_client_destroy(v9ses->clnt);
|
|
||||||
v9ses->clnt = NULL;
|
|
||||||
}
|
|
||||||
|
|
||||||
#ifdef CONFIG_9P_FSCACHE
|
|
||||||
if (v9ses->fscache) {
|
|
||||||
v9fs_cache_session_put_cookie(v9ses);
|
|
||||||
kfree(v9ses->cachetag);
|
|
||||||
}
|
|
||||||
#endif
|
|
||||||
kfree(v9ses->uname);
|
|
||||||
kfree(v9ses->aname);
|
|
||||||
|
|
||||||
bdi_destroy(&v9ses->bdi);
|
|
||||||
|
|
||||||
spin_lock(&v9fs_sessionlist_lock);
|
|
||||||
list_del(&v9ses->slist);
|
|
||||||
spin_unlock(&v9fs_sessionlist_lock);
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* v9fs_session_cancel - terminate a session
|
|
||||||
* @v9ses: session to terminate
|
|
||||||
*
|
|
||||||
* mark transport as disconnected and cancel all pending requests.
|
|
||||||
*/
|
|
||||||
|
|
||||||
void v9fs_session_cancel(struct v9fs_session_info *v9ses) {
|
|
||||||
p9_debug(P9_DEBUG_ERROR, "cancel session %p\n", v9ses);
|
|
||||||
p9_client_disconnect(v9ses->clnt);
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* v9fs_session_begin_cancel - Begin terminate of a session
|
|
||||||
* @v9ses: session to terminate
|
|
||||||
*
|
|
||||||
* After this call we don't allow any request other than clunk.
|
|
||||||
*/
|
|
||||||
|
|
||||||
void v9fs_session_begin_cancel(struct v9fs_session_info *v9ses)
|
|
||||||
{
|
|
||||||
p9_debug(P9_DEBUG_ERROR, "begin cancel session %p\n", v9ses);
|
|
||||||
p9_client_begin_disconnect(v9ses->clnt);
|
|
||||||
}
|
|
||||||
|
|
||||||
extern int v9fs_error_init(void);
|
|
||||||
|
|
||||||
static struct kobject *v9fs_kobj;
|
|
||||||
|
|
||||||
#ifdef CONFIG_9P_FSCACHE
|
|
||||||
/**
|
|
||||||
* caches_show - list caches associated with a session
|
|
||||||
*
|
|
||||||
* Returns the size of buffer written.
|
|
||||||
*/
|
|
||||||
|
|
||||||
static ssize_t caches_show(struct kobject *kobj,
|
|
||||||
struct kobj_attribute *attr,
|
|
||||||
char *buf)
|
|
||||||
{
|
|
||||||
ssize_t n = 0, count = 0, limit = PAGE_SIZE;
|
|
||||||
struct v9fs_session_info *v9ses;
|
|
||||||
|
|
||||||
spin_lock(&v9fs_sessionlist_lock);
|
|
||||||
list_for_each_entry(v9ses, &v9fs_sessionlist, slist) {
|
|
||||||
if (v9ses->cachetag) {
|
|
||||||
n = snprintf(buf, limit, "%s\n", v9ses->cachetag);
|
|
||||||
if (n < 0) {
|
|
||||||
count = n;
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
|
|
||||||
count += n;
|
|
||||||
limit -= n;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
spin_unlock(&v9fs_sessionlist_lock);
|
|
||||||
return count;
|
|
||||||
}
|
|
||||||
|
|
||||||
static struct kobj_attribute v9fs_attr_cache = __ATTR_RO(caches);
|
|
||||||
#endif /* CONFIG_9P_FSCACHE */
|
|
||||||
|
|
||||||
static struct attribute *v9fs_attrs[] = {
|
|
||||||
#ifdef CONFIG_9P_FSCACHE
|
|
||||||
&v9fs_attr_cache.attr,
|
|
||||||
#endif
|
|
||||||
NULL,
|
|
||||||
};
|
|
||||||
|
|
||||||
static struct attribute_group v9fs_attr_group = {
|
|
||||||
.attrs = v9fs_attrs,
|
|
||||||
};
|
|
||||||
|
|
||||||
/**
|
|
||||||
* v9fs_sysfs_init - Initialize the v9fs sysfs interface
|
|
||||||
*
|
|
||||||
*/
|
|
||||||
|
|
||||||
static int v9fs_sysfs_init(void)
|
|
||||||
{
|
|
||||||
v9fs_kobj = kobject_create_and_add("9p", fs_kobj);
|
|
||||||
if (!v9fs_kobj)
|
|
||||||
return -ENOMEM;
|
|
||||||
|
|
||||||
if (sysfs_create_group(v9fs_kobj, &v9fs_attr_group)) {
|
|
||||||
kobject_put(v9fs_kobj);
|
|
||||||
return -ENOMEM;
|
|
||||||
}
|
|
||||||
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* v9fs_sysfs_cleanup - Unregister the v9fs sysfs interface
|
|
||||||
*
|
|
||||||
*/
|
|
||||||
|
|
||||||
static void v9fs_sysfs_cleanup(void)
|
|
||||||
{
|
|
||||||
sysfs_remove_group(v9fs_kobj, &v9fs_attr_group);
|
|
||||||
kobject_put(v9fs_kobj);
|
|
||||||
}
|
|
||||||
|
|
||||||
static void v9fs_inode_init_once(void *foo)
|
|
||||||
{
|
|
||||||
struct v9fs_inode *v9inode = (struct v9fs_inode *)foo;
|
|
||||||
#ifdef CONFIG_9P_FSCACHE
|
|
||||||
v9inode->fscache = NULL;
|
|
||||||
#endif
|
|
||||||
memset(&v9inode->qid, 0, sizeof(v9inode->qid));
|
|
||||||
inode_init_once(&v9inode->vfs_inode);
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* v9fs_init_inode_cache - initialize a cache for 9P
|
|
||||||
* Returns 0 on success.
|
|
||||||
*/
|
|
||||||
static int v9fs_init_inode_cache(void)
|
|
||||||
{
|
|
||||||
v9fs_inode_cache = kmem_cache_create("v9fs_inode_cache",
|
|
||||||
sizeof(struct v9fs_inode),
|
|
||||||
0, (SLAB_RECLAIM_ACCOUNT|
|
|
||||||
SLAB_MEM_SPREAD),
|
|
||||||
v9fs_inode_init_once);
|
|
||||||
if (!v9fs_inode_cache)
|
|
||||||
return -ENOMEM;
|
|
||||||
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* v9fs_destroy_inode_cache - destroy the cache of 9P inode
|
|
||||||
*
|
|
||||||
*/
|
|
||||||
static void v9fs_destroy_inode_cache(void)
|
|
||||||
{
|
|
||||||
/*
|
|
||||||
* Make sure all delayed rcu free inodes are flushed before we
|
|
||||||
* destroy cache.
|
|
||||||
*/
|
|
||||||
rcu_barrier();
|
|
||||||
kmem_cache_destroy(v9fs_inode_cache);
|
|
||||||
}
|
|
||||||
|
|
||||||
static int v9fs_cache_register(void)
|
|
||||||
{
|
|
||||||
int ret;
|
|
||||||
ret = v9fs_init_inode_cache();
|
|
||||||
if (ret < 0)
|
|
||||||
return ret;
|
|
||||||
#ifdef CONFIG_9P_FSCACHE
|
|
||||||
return fscache_register_netfs(&v9fs_cache_netfs);
|
|
||||||
#else
|
|
||||||
return ret;
|
|
||||||
#endif
|
|
||||||
}
|
|
||||||
|
|
||||||
static void v9fs_cache_unregister(void)
|
|
||||||
{
|
|
||||||
v9fs_destroy_inode_cache();
|
|
||||||
#ifdef CONFIG_9P_FSCACHE
|
|
||||||
fscache_unregister_netfs(&v9fs_cache_netfs);
|
|
||||||
#endif
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* init_v9fs - Initialize module
|
|
||||||
*
|
|
||||||
*/
|
|
||||||
|
|
||||||
static int __init init_v9fs(void)
|
|
||||||
{
|
|
||||||
int err;
|
|
||||||
pr_info("Installing v9fs 9p2000 file system support\n");
|
|
||||||
/* TODO: Setup list of registered trasnport modules */
|
|
||||||
|
|
||||||
err = v9fs_cache_register();
|
|
||||||
if (err < 0) {
|
|
||||||
pr_err("Failed to register v9fs for caching\n");
|
|
||||||
return err;
|
|
||||||
}
|
|
||||||
|
|
||||||
err = v9fs_sysfs_init();
|
|
||||||
if (err < 0) {
|
|
||||||
pr_err("Failed to register with sysfs\n");
|
|
||||||
goto out_cache;
|
|
||||||
}
|
|
||||||
err = register_filesystem(&v9fs_fs_type);
|
|
||||||
if (err < 0) {
|
|
||||||
pr_err("Failed to register filesystem\n");
|
|
||||||
goto out_sysfs_cleanup;
|
|
||||||
}
|
|
||||||
|
|
||||||
return 0;
|
|
||||||
|
|
||||||
out_sysfs_cleanup:
|
|
||||||
v9fs_sysfs_cleanup();
|
|
||||||
|
|
||||||
out_cache:
|
|
||||||
v9fs_cache_unregister();
|
|
||||||
|
|
||||||
return err;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* exit_v9fs - shutdown module
|
|
||||||
*
|
|
||||||
*/
|
|
||||||
|
|
||||||
static void __exit exit_v9fs(void)
|
|
||||||
{
|
|
||||||
v9fs_sysfs_cleanup();
|
|
||||||
v9fs_cache_unregister();
|
|
||||||
unregister_filesystem(&v9fs_fs_type);
|
|
||||||
}
|
|
||||||
|
|
||||||
module_init(init_v9fs)
|
|
||||||
module_exit(exit_v9fs)
|
|
||||||
|
|
||||||
MODULE_AUTHOR("Latchesar Ionkov <lucho@ionkov.net>");
|
|
||||||
MODULE_AUTHOR("Eric Van Hensbergen <ericvh@gmail.com>");
|
|
||||||
MODULE_AUTHOR("Ron Minnich <rminnich@lanl.gov>");
|
|
||||||
MODULE_LICENSE("GPL");
|
|
@ -1,227 +0,0 @@
|
|||||||
/*
|
|
||||||
* V9FS definitions.
|
|
||||||
*
|
|
||||||
* Copyright (C) 2004-2008 by Eric Van Hensbergen <ericvh@gmail.com>
|
|
||||||
* Copyright (C) 2002 by Ron Minnich <rminnich@lanl.gov>
|
|
||||||
*
|
|
||||||
* This program is free software; you can redistribute it and/or modify
|
|
||||||
* it under the terms of the GNU General Public License version 2
|
|
||||||
* as published by the Free Software Foundation.
|
|
||||||
*
|
|
||||||
* This program is distributed in the hope that it will be useful,
|
|
||||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
||||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
||||||
* GNU General Public License for more details.
|
|
||||||
*
|
|
||||||
* You should have received a copy of the GNU General Public License
|
|
||||||
* along with this program; if not, write to:
|
|
||||||
* Free Software Foundation
|
|
||||||
* 51 Franklin Street, Fifth Floor
|
|
||||||
* Boston, MA 02111-1301 USA
|
|
||||||
*
|
|
||||||
*/
|
|
||||||
#ifndef FS_9P_V9FS_H
|
|
||||||
#define FS_9P_V9FS_H
|
|
||||||
|
|
||||||
#include <linux/backing-dev.h>
|
|
||||||
|
|
||||||
/**
|
|
||||||
* enum p9_session_flags - option flags for each 9P session
|
|
||||||
* @V9FS_PROTO_2000U: whether or not to use 9P2000.u extensions
|
|
||||||
* @V9FS_PROTO_2000L: whether or not to use 9P2000.l extensions
|
|
||||||
* @V9FS_ACCESS_SINGLE: only the mounting user can access the hierarchy
|
|
||||||
* @V9FS_ACCESS_USER: a new attach will be issued for every user (default)
|
|
||||||
* @V9FS_ACCESS_CLIENT: Just like user, but access check is performed on client.
|
|
||||||
* @V9FS_ACCESS_ANY: use a single attach for all users
|
|
||||||
* @V9FS_ACCESS_MASK: bit mask of different ACCESS options
|
|
||||||
* @V9FS_POSIX_ACL: POSIX ACLs are enforced
|
|
||||||
*
|
|
||||||
* Session flags reflect options selected by users at mount time
|
|
||||||
*/
|
|
||||||
#define V9FS_ACCESS_ANY (V9FS_ACCESS_SINGLE | \
|
|
||||||
V9FS_ACCESS_USER | \
|
|
||||||
V9FS_ACCESS_CLIENT)
|
|
||||||
#define V9FS_ACCESS_MASK V9FS_ACCESS_ANY
|
|
||||||
#define V9FS_ACL_MASK V9FS_POSIX_ACL
|
|
||||||
|
|
||||||
enum p9_session_flags {
|
|
||||||
V9FS_PROTO_2000U = 0x01,
|
|
||||||
V9FS_PROTO_2000L = 0x02,
|
|
||||||
V9FS_ACCESS_SINGLE = 0x04,
|
|
||||||
V9FS_ACCESS_USER = 0x08,
|
|
||||||
V9FS_ACCESS_CLIENT = 0x10,
|
|
||||||
V9FS_POSIX_ACL = 0x20
|
|
||||||
};
|
|
||||||
|
|
||||||
/* possible values of ->cache */
|
|
||||||
/**
|
|
||||||
* enum p9_cache_modes - user specified cache preferences
|
|
||||||
* @CACHE_NONE: do not cache data, dentries, or directory contents (default)
|
|
||||||
* @CACHE_LOOSE: cache data, dentries, and directory contents w/no consistency
|
|
||||||
*
|
|
||||||
* eventually support loose, tight, time, session, default always none
|
|
||||||
*/
|
|
||||||
|
|
||||||
enum p9_cache_modes {
|
|
||||||
CACHE_NONE,
|
|
||||||
CACHE_LOOSE,
|
|
||||||
CACHE_FSCACHE,
|
|
||||||
};
|
|
||||||
|
|
||||||
/**
|
|
||||||
* struct v9fs_session_info - per-instance session information
|
|
||||||
* @flags: session options of type &p9_session_flags
|
|
||||||
* @nodev: set to 1 to disable device mapping
|
|
||||||
* @debug: debug level
|
|
||||||
* @afid: authentication handle
|
|
||||||
* @cache: cache mode of type &p9_cache_modes
|
|
||||||
* @cachetag: the tag of the cache associated with this session
|
|
||||||
* @fscache: session cookie associated with FS-Cache
|
|
||||||
* @options: copy of options string given by user
|
|
||||||
* @uname: string user name to mount hierarchy as
|
|
||||||
* @aname: mount specifier for remote hierarchy
|
|
||||||
* @maxdata: maximum data to be sent/recvd per protocol message
|
|
||||||
* @dfltuid: default numeric userid to mount hierarchy as
|
|
||||||
* @dfltgid: default numeric groupid to mount hierarchy as
|
|
||||||
* @uid: if %V9FS_ACCESS_SINGLE, the numeric uid which mounted the hierarchy
|
|
||||||
* @clnt: reference to 9P network client instantiated for this session
|
|
||||||
* @slist: reference to list of registered 9p sessions
|
|
||||||
*
|
|
||||||
* This structure holds state for each session instance established during
|
|
||||||
* a sys_mount() .
|
|
||||||
*
|
|
||||||
* Bugs: there seems to be a lot of state which could be condensed and/or
|
|
||||||
* removed.
|
|
||||||
*/
|
|
||||||
|
|
||||||
struct v9fs_session_info {
|
|
||||||
/* options */
|
|
||||||
unsigned char flags;
|
|
||||||
unsigned char nodev;
|
|
||||||
unsigned short debug;
|
|
||||||
unsigned int afid;
|
|
||||||
unsigned int cache;
|
|
||||||
#ifdef CONFIG_9P_FSCACHE
|
|
||||||
char *cachetag;
|
|
||||||
struct fscache_cookie *fscache;
|
|
||||||
#endif
|
|
||||||
|
|
||||||
char *uname; /* user name to mount as */
|
|
||||||
char *aname; /* name of remote hierarchy being mounted */
|
|
||||||
unsigned int maxdata; /* max data for client interface */
|
|
||||||
kuid_t dfltuid; /* default uid/muid for legacy support */
|
|
||||||
kgid_t dfltgid; /* default gid for legacy support */
|
|
||||||
kuid_t uid; /* if ACCESS_SINGLE, the uid that has access */
|
|
||||||
struct p9_client *clnt; /* 9p client */
|
|
||||||
struct list_head slist; /* list of sessions registered with v9fs */
|
|
||||||
struct backing_dev_info bdi;
|
|
||||||
struct rw_semaphore rename_sem;
|
|
||||||
};
|
|
||||||
|
|
||||||
/* cache_validity flags */
|
|
||||||
#define V9FS_INO_INVALID_ATTR 0x01
|
|
||||||
|
|
||||||
struct v9fs_inode {
|
|
||||||
#ifdef CONFIG_9P_FSCACHE
|
|
||||||
spinlock_t fscache_lock;
|
|
||||||
struct fscache_cookie *fscache;
|
|
||||||
#endif
|
|
||||||
struct p9_qid qid;
|
|
||||||
unsigned int cache_validity;
|
|
||||||
struct p9_fid *writeback_fid;
|
|
||||||
struct mutex v_mutex;
|
|
||||||
struct inode vfs_inode;
|
|
||||||
};
|
|
||||||
|
|
||||||
static inline struct v9fs_inode *V9FS_I(const struct inode *inode)
|
|
||||||
{
|
|
||||||
return container_of(inode, struct v9fs_inode, vfs_inode);
|
|
||||||
}
|
|
||||||
|
|
||||||
struct p9_fid *v9fs_session_init(struct v9fs_session_info *, const char *,
|
|
||||||
char *);
|
|
||||||
extern void v9fs_session_close(struct v9fs_session_info *v9ses);
|
|
||||||
extern void v9fs_session_cancel(struct v9fs_session_info *v9ses);
|
|
||||||
extern void v9fs_session_begin_cancel(struct v9fs_session_info *v9ses);
|
|
||||||
extern struct dentry *v9fs_vfs_lookup(struct inode *dir, struct dentry *dentry,
|
|
||||||
unsigned int flags);
|
|
||||||
extern int v9fs_vfs_unlink(struct inode *i, struct dentry *d);
|
|
||||||
extern int v9fs_vfs_rmdir(struct inode *i, struct dentry *d);
|
|
||||||
extern int v9fs_vfs_rename(struct inode *old_dir, struct dentry *old_dentry,
|
|
||||||
struct inode *new_dir, struct dentry *new_dentry);
|
|
||||||
extern void v9fs_vfs_put_link(struct dentry *dentry, struct nameidata *nd,
|
|
||||||
void *p);
|
|
||||||
extern struct inode *v9fs_inode_from_fid(struct v9fs_session_info *v9ses,
|
|
||||||
struct p9_fid *fid,
|
|
||||||
struct super_block *sb, int new);
|
|
||||||
extern const struct inode_operations v9fs_dir_inode_operations_dotl;
|
|
||||||
extern const struct inode_operations v9fs_file_inode_operations_dotl;
|
|
||||||
extern const struct inode_operations v9fs_symlink_inode_operations_dotl;
|
|
||||||
extern struct inode *v9fs_inode_from_fid_dotl(struct v9fs_session_info *v9ses,
|
|
||||||
struct p9_fid *fid,
|
|
||||||
struct super_block *sb, int new);
|
|
||||||
|
|
||||||
/* other default globals */
|
|
||||||
#define V9FS_PORT 564
|
|
||||||
#define V9FS_DEFUSER "nobody"
|
|
||||||
#define V9FS_DEFANAME ""
|
|
||||||
#define V9FS_DEFUID KUIDT_INIT(-2)
|
|
||||||
#define V9FS_DEFGID KGIDT_INIT(-2)
|
|
||||||
|
|
||||||
static inline struct v9fs_session_info *v9fs_inode2v9ses(struct inode *inode)
|
|
||||||
{
|
|
||||||
return (inode->i_sb->s_fs_info);
|
|
||||||
}
|
|
||||||
|
|
||||||
static inline struct v9fs_session_info *v9fs_dentry2v9ses(struct dentry *dentry)
|
|
||||||
{
|
|
||||||
return dentry->d_sb->s_fs_info;
|
|
||||||
}
|
|
||||||
|
|
||||||
static inline int v9fs_proto_dotu(struct v9fs_session_info *v9ses)
|
|
||||||
{
|
|
||||||
return v9ses->flags & V9FS_PROTO_2000U;
|
|
||||||
}
|
|
||||||
|
|
||||||
static inline int v9fs_proto_dotl(struct v9fs_session_info *v9ses)
|
|
||||||
{
|
|
||||||
return v9ses->flags & V9FS_PROTO_2000L;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* v9fs_get_inode_from_fid - Helper routine to populate an inode by
|
|
||||||
* issuing a attribute request
|
|
||||||
* @v9ses: session information
|
|
||||||
* @fid: fid to issue attribute request for
|
|
||||||
* @sb: superblock on which to create inode
|
|
||||||
*
|
|
||||||
*/
|
|
||||||
static inline struct inode *
|
|
||||||
v9fs_get_inode_from_fid(struct v9fs_session_info *v9ses, struct p9_fid *fid,
|
|
||||||
struct super_block *sb)
|
|
||||||
{
|
|
||||||
if (v9fs_proto_dotl(v9ses))
|
|
||||||
return v9fs_inode_from_fid_dotl(v9ses, fid, sb, 0);
|
|
||||||
else
|
|
||||||
return v9fs_inode_from_fid(v9ses, fid, sb, 0);
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* v9fs_get_new_inode_from_fid - Helper routine to populate an inode by
|
|
||||||
* issuing a attribute request
|
|
||||||
* @v9ses: session information
|
|
||||||
* @fid: fid to issue attribute request for
|
|
||||||
* @sb: superblock on which to create inode
|
|
||||||
*
|
|
||||||
*/
|
|
||||||
static inline struct inode *
|
|
||||||
v9fs_get_new_inode_from_fid(struct v9fs_session_info *v9ses, struct p9_fid *fid,
|
|
||||||
struct super_block *sb)
|
|
||||||
{
|
|
||||||
if (v9fs_proto_dotl(v9ses))
|
|
||||||
return v9fs_inode_from_fid_dotl(v9ses, fid, sb, 1);
|
|
||||||
else
|
|
||||||
return v9fs_inode_from_fid(v9ses, fid, sb, 1);
|
|
||||||
}
|
|
||||||
|
|
||||||
#endif
|
|
@ -1,88 +0,0 @@
|
|||||||
/*
|
|
||||||
* V9FS VFS extensions.
|
|
||||||
*
|
|
||||||
* Copyright (C) 2004 by Eric Van Hensbergen <ericvh@gmail.com>
|
|
||||||
* Copyright (C) 2002 by Ron Minnich <rminnich@lanl.gov>
|
|
||||||
*
|
|
||||||
* This program is free software; you can redistribute it and/or modify
|
|
||||||
* it under the terms of the GNU General Public License version 2
|
|
||||||
* as published by the Free Software Foundation.
|
|
||||||
*
|
|
||||||
* This program is distributed in the hope that it will be useful,
|
|
||||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
||||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
||||||
* GNU General Public License for more details.
|
|
||||||
*
|
|
||||||
* You should have received a copy of the GNU General Public License
|
|
||||||
* along with this program; if not, write to:
|
|
||||||
* Free Software Foundation
|
|
||||||
* 51 Franklin Street, Fifth Floor
|
|
||||||
* Boston, MA 02111-1301 USA
|
|
||||||
*
|
|
||||||
*/
|
|
||||||
#ifndef FS_9P_V9FS_VFS_H
|
|
||||||
#define FS_9P_V9FS_VFS_H
|
|
||||||
|
|
||||||
/* plan9 semantics are that created files are implicitly opened.
|
|
||||||
* But linux semantics are that you call create, then open.
|
|
||||||
* the plan9 approach is superior as it provides an atomic
|
|
||||||
* open.
|
|
||||||
* we track the create fid here. When the file is opened, if fidopen is
|
|
||||||
* non-zero, we use the fid and can skip some steps.
|
|
||||||
* there may be a better way to do this, but I don't know it.
|
|
||||||
* one BAD way is to clunk the fid on create, then open it again:
|
|
||||||
* you lose the atomicity of file open
|
|
||||||
*/
|
|
||||||
|
|
||||||
/* special case:
|
|
||||||
* unlink calls remove, which is an implicit clunk. So we have to track
|
|
||||||
* that kind of thing so that we don't try to clunk a dead fid.
|
|
||||||
*/
|
|
||||||
#define P9_LOCK_TIMEOUT (30*HZ)
|
|
||||||
|
|
||||||
extern struct file_system_type v9fs_fs_type;
|
|
||||||
extern const struct address_space_operations v9fs_addr_operations;
|
|
||||||
extern const struct file_operations v9fs_file_operations;
|
|
||||||
extern const struct file_operations v9fs_file_operations_dotl;
|
|
||||||
extern const struct file_operations v9fs_dir_operations;
|
|
||||||
extern const struct file_operations v9fs_dir_operations_dotl;
|
|
||||||
extern const struct dentry_operations v9fs_dentry_operations;
|
|
||||||
extern const struct dentry_operations v9fs_cached_dentry_operations;
|
|
||||||
extern const struct file_operations v9fs_cached_file_operations;
|
|
||||||
extern const struct file_operations v9fs_cached_file_operations_dotl;
|
|
||||||
extern struct kmem_cache *v9fs_inode_cache;
|
|
||||||
|
|
||||||
struct inode *v9fs_alloc_inode(struct super_block *sb);
|
|
||||||
void v9fs_destroy_inode(struct inode *inode);
|
|
||||||
struct inode *v9fs_get_inode(struct super_block *sb, umode_t mode, dev_t);
|
|
||||||
int v9fs_init_inode(struct v9fs_session_info *v9ses,
|
|
||||||
struct inode *inode, umode_t mode, dev_t);
|
|
||||||
void v9fs_evict_inode(struct inode *inode);
|
|
||||||
ino_t v9fs_qid2ino(struct p9_qid *qid);
|
|
||||||
void v9fs_stat2inode(struct p9_wstat *, struct inode *, struct super_block *);
|
|
||||||
void v9fs_stat2inode_dotl(struct p9_stat_dotl *, struct inode *);
|
|
||||||
int v9fs_dir_release(struct inode *inode, struct file *filp);
|
|
||||||
int v9fs_file_open(struct inode *inode, struct file *file);
|
|
||||||
void v9fs_inode2stat(struct inode *inode, struct p9_wstat *stat);
|
|
||||||
int v9fs_uflags2omode(int uflags, int extended);
|
|
||||||
|
|
||||||
ssize_t v9fs_file_readn(struct file *, char *, char __user *, u32, u64);
|
|
||||||
ssize_t v9fs_fid_readn(struct p9_fid *, char *, char __user *, u32, u64);
|
|
||||||
void v9fs_blank_wstat(struct p9_wstat *wstat);
|
|
||||||
int v9fs_vfs_setattr_dotl(struct dentry *, struct iattr *);
|
|
||||||
int v9fs_file_fsync_dotl(struct file *filp, loff_t start, loff_t end,
|
|
||||||
int datasync);
|
|
||||||
ssize_t v9fs_file_write_internal(struct inode *, struct p9_fid *,
|
|
||||||
const char __user *, size_t, loff_t *, int);
|
|
||||||
int v9fs_refresh_inode(struct p9_fid *fid, struct inode *inode);
|
|
||||||
int v9fs_refresh_inode_dotl(struct p9_fid *fid, struct inode *inode);
|
|
||||||
static inline void v9fs_invalidate_inode_attr(struct inode *inode)
|
|
||||||
{
|
|
||||||
struct v9fs_inode *v9inode;
|
|
||||||
v9inode = V9FS_I(inode);
|
|
||||||
v9inode->cache_validity |= V9FS_INO_INVALID_ATTR;
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
int v9fs_open_to_dotl_flags(int flags);
|
|
||||||
#endif
|
|
@ -1,353 +0,0 @@
|
|||||||
/*
|
|
||||||
* linux/fs/9p/vfs_addr.c
|
|
||||||
*
|
|
||||||
* This file contians vfs address (mmap) ops for 9P2000.
|
|
||||||
*
|
|
||||||
* Copyright (C) 2005 by Eric Van Hensbergen <ericvh@gmail.com>
|
|
||||||
* Copyright (C) 2002 by Ron Minnich <rminnich@lanl.gov>
|
|
||||||
*
|
|
||||||
* This program is free software; you can redistribute it and/or modify
|
|
||||||
* it under the terms of the GNU General Public License version 2
|
|
||||||
* as published by the Free Software Foundation.
|
|
||||||
*
|
|
||||||
* This program is distributed in the hope that it will be useful,
|
|
||||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
||||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
||||||
* GNU General Public License for more details.
|
|
||||||
*
|
|
||||||
* You should have received a copy of the GNU General Public License
|
|
||||||
* along with this program; if not, write to:
|
|
||||||
* Free Software Foundation
|
|
||||||
* 51 Franklin Street, Fifth Floor
|
|
||||||
* Boston, MA 02111-1301 USA
|
|
||||||
*
|
|
||||||
*/
|
|
||||||
|
|
||||||
#include <linux/module.h>
|
|
||||||
#include <linux/errno.h>
|
|
||||||
#include <linux/fs.h>
|
|
||||||
#include <linux/file.h>
|
|
||||||
#include <linux/stat.h>
|
|
||||||
#include <linux/string.h>
|
|
||||||
#include <linux/inet.h>
|
|
||||||
#include <linux/pagemap.h>
|
|
||||||
#include <linux/idr.h>
|
|
||||||
#include <linux/sched.h>
|
|
||||||
#include <linux/aio.h>
|
|
||||||
#include <net/9p/9p.h>
|
|
||||||
#include <net/9p/client.h>
|
|
||||||
|
|
||||||
#include "v9fs.h"
|
|
||||||
#include "v9fs_vfs.h"
|
|
||||||
#include "cache.h"
|
|
||||||
#include "fid.h"
|
|
||||||
|
|
||||||
/**
|
|
||||||
* v9fs_fid_readpage - read an entire page in from 9P
|
|
||||||
*
|
|
||||||
* @fid: fid being read
|
|
||||||
* @page: structure to page
|
|
||||||
*
|
|
||||||
*/
|
|
||||||
static int v9fs_fid_readpage(struct p9_fid *fid, struct page *page)
|
|
||||||
{
|
|
||||||
int retval;
|
|
||||||
loff_t offset;
|
|
||||||
char *buffer;
|
|
||||||
struct inode *inode;
|
|
||||||
|
|
||||||
inode = page->mapping->host;
|
|
||||||
p9_debug(P9_DEBUG_VFS, "\n");
|
|
||||||
|
|
||||||
BUG_ON(!PageLocked(page));
|
|
||||||
|
|
||||||
retval = v9fs_readpage_from_fscache(inode, page);
|
|
||||||
if (retval == 0)
|
|
||||||
return retval;
|
|
||||||
|
|
||||||
buffer = kmap(page);
|
|
||||||
offset = page_offset(page);
|
|
||||||
|
|
||||||
retval = v9fs_fid_readn(fid, buffer, NULL, PAGE_CACHE_SIZE, offset);
|
|
||||||
if (retval < 0) {
|
|
||||||
v9fs_uncache_page(inode, page);
|
|
||||||
goto done;
|
|
||||||
}
|
|
||||||
|
|
||||||
memset(buffer + retval, 0, PAGE_CACHE_SIZE - retval);
|
|
||||||
flush_dcache_page(page);
|
|
||||||
SetPageUptodate(page);
|
|
||||||
|
|
||||||
v9fs_readpage_to_fscache(inode, page);
|
|
||||||
retval = 0;
|
|
||||||
|
|
||||||
done:
|
|
||||||
kunmap(page);
|
|
||||||
unlock_page(page);
|
|
||||||
return retval;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* v9fs_vfs_readpage - read an entire page in from 9P
|
|
||||||
*
|
|
||||||
* @filp: file being read
|
|
||||||
* @page: structure to page
|
|
||||||
*
|
|
||||||
*/
|
|
||||||
|
|
||||||
static int v9fs_vfs_readpage(struct file *filp, struct page *page)
|
|
||||||
{
|
|
||||||
return v9fs_fid_readpage(filp->private_data, page);
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* v9fs_vfs_readpages - read a set of pages from 9P
|
|
||||||
*
|
|
||||||
* @filp: file being read
|
|
||||||
* @mapping: the address space
|
|
||||||
* @pages: list of pages to read
|
|
||||||
* @nr_pages: count of pages to read
|
|
||||||
*
|
|
||||||
*/
|
|
||||||
|
|
||||||
static int v9fs_vfs_readpages(struct file *filp, struct address_space *mapping,
|
|
||||||
struct list_head *pages, unsigned nr_pages)
|
|
||||||
{
|
|
||||||
int ret = 0;
|
|
||||||
struct inode *inode;
|
|
||||||
|
|
||||||
inode = mapping->host;
|
|
||||||
p9_debug(P9_DEBUG_VFS, "inode: %p file: %p\n", inode, filp);
|
|
||||||
|
|
||||||
ret = v9fs_readpages_from_fscache(inode, mapping, pages, &nr_pages);
|
|
||||||
if (ret == 0)
|
|
||||||
return ret;
|
|
||||||
|
|
||||||
ret = read_cache_pages(mapping, pages, (void *)v9fs_vfs_readpage, filp);
|
|
||||||
p9_debug(P9_DEBUG_VFS, " = %d\n", ret);
|
|
||||||
return ret;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* v9fs_release_page - release the private state associated with a page
|
|
||||||
*
|
|
||||||
* Returns 1 if the page can be released, false otherwise.
|
|
||||||
*/
|
|
||||||
|
|
||||||
static int v9fs_release_page(struct page *page, gfp_t gfp)
|
|
||||||
{
|
|
||||||
if (PagePrivate(page))
|
|
||||||
return 0;
|
|
||||||
return v9fs_fscache_release_page(page, gfp);
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* v9fs_invalidate_page - Invalidate a page completely or partially
|
|
||||||
*
|
|
||||||
* @page: structure to page
|
|
||||||
* @offset: offset in the page
|
|
||||||
*/
|
|
||||||
|
|
||||||
static void v9fs_invalidate_page(struct page *page, unsigned long offset)
|
|
||||||
{
|
|
||||||
/*
|
|
||||||
* If called with zero offset, we should release
|
|
||||||
* the private state assocated with the page
|
|
||||||
*/
|
|
||||||
if (offset == 0)
|
|
||||||
v9fs_fscache_invalidate_page(page);
|
|
||||||
}
|
|
||||||
|
|
||||||
static int v9fs_vfs_writepage_locked(struct page *page)
|
|
||||||
{
|
|
||||||
char *buffer;
|
|
||||||
int retval, len;
|
|
||||||
loff_t offset, size;
|
|
||||||
mm_segment_t old_fs;
|
|
||||||
struct v9fs_inode *v9inode;
|
|
||||||
struct inode *inode = page->mapping->host;
|
|
||||||
|
|
||||||
v9inode = V9FS_I(inode);
|
|
||||||
size = i_size_read(inode);
|
|
||||||
if (page->index == size >> PAGE_CACHE_SHIFT)
|
|
||||||
len = size & ~PAGE_CACHE_MASK;
|
|
||||||
else
|
|
||||||
len = PAGE_CACHE_SIZE;
|
|
||||||
|
|
||||||
set_page_writeback(page);
|
|
||||||
|
|
||||||
buffer = kmap(page);
|
|
||||||
offset = page_offset(page);
|
|
||||||
|
|
||||||
old_fs = get_fs();
|
|
||||||
set_fs(get_ds());
|
|
||||||
/* We should have writeback_fid always set */
|
|
||||||
BUG_ON(!v9inode->writeback_fid);
|
|
||||||
|
|
||||||
retval = v9fs_file_write_internal(inode,
|
|
||||||
v9inode->writeback_fid,
|
|
||||||
(__force const char __user *)buffer,
|
|
||||||
len, &offset, 0);
|
|
||||||
if (retval > 0)
|
|
||||||
retval = 0;
|
|
||||||
|
|
||||||
set_fs(old_fs);
|
|
||||||
kunmap(page);
|
|
||||||
end_page_writeback(page);
|
|
||||||
return retval;
|
|
||||||
}
|
|
||||||
|
|
||||||
static int v9fs_vfs_writepage(struct page *page, struct writeback_control *wbc)
|
|
||||||
{
|
|
||||||
int retval;
|
|
||||||
|
|
||||||
retval = v9fs_vfs_writepage_locked(page);
|
|
||||||
if (retval < 0) {
|
|
||||||
if (retval == -EAGAIN) {
|
|
||||||
redirty_page_for_writepage(wbc, page);
|
|
||||||
retval = 0;
|
|
||||||
} else {
|
|
||||||
SetPageError(page);
|
|
||||||
mapping_set_error(page->mapping, retval);
|
|
||||||
}
|
|
||||||
} else
|
|
||||||
retval = 0;
|
|
||||||
|
|
||||||
unlock_page(page);
|
|
||||||
return retval;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* v9fs_launder_page - Writeback a dirty page
|
|
||||||
* Returns 0 on success.
|
|
||||||
*/
|
|
||||||
|
|
||||||
static int v9fs_launder_page(struct page *page)
|
|
||||||
{
|
|
||||||
int retval;
|
|
||||||
struct inode *inode = page->mapping->host;
|
|
||||||
|
|
||||||
v9fs_fscache_wait_on_page_write(inode, page);
|
|
||||||
if (clear_page_dirty_for_io(page)) {
|
|
||||||
retval = v9fs_vfs_writepage_locked(page);
|
|
||||||
if (retval)
|
|
||||||
return retval;
|
|
||||||
}
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* v9fs_direct_IO - 9P address space operation for direct I/O
|
|
||||||
* @rw: direction (read or write)
|
|
||||||
* @iocb: target I/O control block
|
|
||||||
* @iov: array of vectors that define I/O buffer
|
|
||||||
* @pos: offset in file to begin the operation
|
|
||||||
* @nr_segs: size of iovec array
|
|
||||||
*
|
|
||||||
* The presence of v9fs_direct_IO() in the address space ops vector
|
|
||||||
* allowes open() O_DIRECT flags which would have failed otherwise.
|
|
||||||
*
|
|
||||||
* In the non-cached mode, we shunt off direct read and write requests before
|
|
||||||
* the VFS gets them, so this method should never be called.
|
|
||||||
*
|
|
||||||
* Direct IO is not 'yet' supported in the cached mode. Hence when
|
|
||||||
* this routine is called through generic_file_aio_read(), the read/write fails
|
|
||||||
* with an error.
|
|
||||||
*
|
|
||||||
*/
|
|
||||||
static ssize_t
|
|
||||||
v9fs_direct_IO(int rw, struct kiocb *iocb, const struct iovec *iov,
|
|
||||||
loff_t pos, unsigned long nr_segs)
|
|
||||||
{
|
|
||||||
/*
|
|
||||||
* FIXME
|
|
||||||
* Now that we do caching with cache mode enabled, We need
|
|
||||||
* to support direct IO
|
|
||||||
*/
|
|
||||||
p9_debug(P9_DEBUG_VFS, "v9fs_direct_IO: v9fs_direct_IO (%s) off/no(%lld/%lu) EINVAL\n",
|
|
||||||
iocb->ki_filp->f_path.dentry->d_name.name,
|
|
||||||
(long long)pos, nr_segs);
|
|
||||||
|
|
||||||
return -EINVAL;
|
|
||||||
}
|
|
||||||
|
|
||||||
static int v9fs_write_begin(struct file *filp, struct address_space *mapping,
|
|
||||||
loff_t pos, unsigned len, unsigned flags,
|
|
||||||
struct page **pagep, void **fsdata)
|
|
||||||
{
|
|
||||||
int retval = 0;
|
|
||||||
struct page *page;
|
|
||||||
struct v9fs_inode *v9inode;
|
|
||||||
pgoff_t index = pos >> PAGE_CACHE_SHIFT;
|
|
||||||
struct inode *inode = mapping->host;
|
|
||||||
|
|
||||||
v9inode = V9FS_I(inode);
|
|
||||||
start:
|
|
||||||
page = grab_cache_page_write_begin(mapping, index, flags);
|
|
||||||
if (!page) {
|
|
||||||
retval = -ENOMEM;
|
|
||||||
goto out;
|
|
||||||
}
|
|
||||||
BUG_ON(!v9inode->writeback_fid);
|
|
||||||
if (PageUptodate(page))
|
|
||||||
goto out;
|
|
||||||
|
|
||||||
if (len == PAGE_CACHE_SIZE)
|
|
||||||
goto out;
|
|
||||||
|
|
||||||
retval = v9fs_fid_readpage(v9inode->writeback_fid, page);
|
|
||||||
page_cache_release(page);
|
|
||||||
if (!retval)
|
|
||||||
goto start;
|
|
||||||
out:
|
|
||||||
*pagep = page;
|
|
||||||
return retval;
|
|
||||||
}
|
|
||||||
|
|
||||||
static int v9fs_write_end(struct file *filp, struct address_space *mapping,
|
|
||||||
loff_t pos, unsigned len, unsigned copied,
|
|
||||||
struct page *page, void *fsdata)
|
|
||||||
{
|
|
||||||
loff_t last_pos = pos + copied;
|
|
||||||
struct inode *inode = page->mapping->host;
|
|
||||||
|
|
||||||
if (unlikely(copied < len)) {
|
|
||||||
/*
|
|
||||||
* zero out the rest of the area
|
|
||||||
*/
|
|
||||||
unsigned from = pos & (PAGE_CACHE_SIZE - 1);
|
|
||||||
|
|
||||||
zero_user(page, from + copied, len - copied);
|
|
||||||
flush_dcache_page(page);
|
|
||||||
}
|
|
||||||
|
|
||||||
if (!PageUptodate(page))
|
|
||||||
SetPageUptodate(page);
|
|
||||||
/*
|
|
||||||
* No need to use i_size_read() here, the i_size
|
|
||||||
* cannot change under us because we hold the i_mutex.
|
|
||||||
*/
|
|
||||||
if (last_pos > inode->i_size) {
|
|
||||||
inode_add_bytes(inode, last_pos - inode->i_size);
|
|
||||||
i_size_write(inode, last_pos);
|
|
||||||
}
|
|
||||||
set_page_dirty(page);
|
|
||||||
unlock_page(page);
|
|
||||||
page_cache_release(page);
|
|
||||||
|
|
||||||
return copied;
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
const struct address_space_operations v9fs_addr_operations = {
|
|
||||||
.readpage = v9fs_vfs_readpage,
|
|
||||||
.readpages = v9fs_vfs_readpages,
|
|
||||||
.set_page_dirty = __set_page_dirty_nobuffers,
|
|
||||||
.writepage = v9fs_vfs_writepage,
|
|
||||||
.write_begin = v9fs_write_begin,
|
|
||||||
.write_end = v9fs_write_end,
|
|
||||||
.releasepage = v9fs_release_page,
|
|
||||||
.invalidatepage = v9fs_invalidate_page,
|
|
||||||
.launder_page = v9fs_launder_page,
|
|
||||||
.direct_IO = v9fs_direct_IO,
|
|
||||||
};
|
|
@ -1,139 +0,0 @@
|
|||||||
/*
|
|
||||||
* linux/fs/9p/vfs_dentry.c
|
|
||||||
*
|
|
||||||
* This file contians vfs dentry ops for the 9P2000 protocol.
|
|
||||||
*
|
|
||||||
* Copyright (C) 2004 by Eric Van Hensbergen <ericvh@gmail.com>
|
|
||||||
* Copyright (C) 2002 by Ron Minnich <rminnich@lanl.gov>
|
|
||||||
*
|
|
||||||
* This program is free software; you can redistribute it and/or modify
|
|
||||||
* it under the terms of the GNU General Public License version 2
|
|
||||||
* as published by the Free Software Foundation.
|
|
||||||
*
|
|
||||||
* This program is distributed in the hope that it will be useful,
|
|
||||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
||||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
||||||
* GNU General Public License for more details.
|
|
||||||
*
|
|
||||||
* You should have received a copy of the GNU General Public License
|
|
||||||
* along with this program; if not, write to:
|
|
||||||
* Free Software Foundation
|
|
||||||
* 51 Franklin Street, Fifth Floor
|
|
||||||
* Boston, MA 02111-1301 USA
|
|
||||||
*
|
|
||||||
*/
|
|
||||||
|
|
||||||
#include <linux/module.h>
|
|
||||||
#include <linux/errno.h>
|
|
||||||
#include <linux/fs.h>
|
|
||||||
#include <linux/file.h>
|
|
||||||
#include <linux/pagemap.h>
|
|
||||||
#include <linux/stat.h>
|
|
||||||
#include <linux/string.h>
|
|
||||||
#include <linux/inet.h>
|
|
||||||
#include <linux/namei.h>
|
|
||||||
#include <linux/idr.h>
|
|
||||||
#include <linux/sched.h>
|
|
||||||
#include <linux/slab.h>
|
|
||||||
#include <net/9p/9p.h>
|
|
||||||
#include <net/9p/client.h>
|
|
||||||
|
|
||||||
#include "v9fs.h"
|
|
||||||
#include "v9fs_vfs.h"
|
|
||||||
#include "fid.h"
|
|
||||||
|
|
||||||
/**
|
|
||||||
* v9fs_dentry_delete - called when dentry refcount equals 0
|
|
||||||
* @dentry: dentry in question
|
|
||||||
*
|
|
||||||
* By returning 1 here we should remove cacheing of unused
|
|
||||||
* dentry components.
|
|
||||||
*
|
|
||||||
*/
|
|
||||||
|
|
||||||
static int v9fs_dentry_delete(const struct dentry *dentry)
|
|
||||||
{
|
|
||||||
p9_debug(P9_DEBUG_VFS, " dentry: %s (%p)\n",
|
|
||||||
dentry->d_name.name, dentry);
|
|
||||||
|
|
||||||
return 1;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* v9fs_cached_dentry_delete - called when dentry refcount equals 0
|
|
||||||
* @dentry: dentry in question
|
|
||||||
*
|
|
||||||
*/
|
|
||||||
static int v9fs_cached_dentry_delete(const struct dentry *dentry)
|
|
||||||
{
|
|
||||||
p9_debug(P9_DEBUG_VFS, " dentry: %s (%p)\n",
|
|
||||||
dentry->d_name.name, dentry);
|
|
||||||
|
|
||||||
/* Don't cache negative dentries */
|
|
||||||
if (!dentry->d_inode)
|
|
||||||
return 1;
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* v9fs_dentry_release - called when dentry is going to be freed
|
|
||||||
* @dentry: dentry that is being release
|
|
||||||
*
|
|
||||||
*/
|
|
||||||
|
|
||||||
static void v9fs_dentry_release(struct dentry *dentry)
|
|
||||||
{
|
|
||||||
struct hlist_node *p, *n;
|
|
||||||
p9_debug(P9_DEBUG_VFS, " dentry: %s (%p)\n",
|
|
||||||
dentry->d_name.name, dentry);
|
|
||||||
hlist_for_each_safe(p, n, (struct hlist_head *)&dentry->d_fsdata)
|
|
||||||
p9_client_clunk(hlist_entry(p, struct p9_fid, dlist));
|
|
||||||
dentry->d_fsdata = NULL;
|
|
||||||
}
|
|
||||||
|
|
||||||
static int v9fs_lookup_revalidate(struct dentry *dentry, unsigned int flags)
|
|
||||||
{
|
|
||||||
struct p9_fid *fid;
|
|
||||||
struct inode *inode;
|
|
||||||
struct v9fs_inode *v9inode;
|
|
||||||
|
|
||||||
if (flags & LOOKUP_RCU)
|
|
||||||
return -ECHILD;
|
|
||||||
|
|
||||||
inode = dentry->d_inode;
|
|
||||||
if (!inode)
|
|
||||||
goto out_valid;
|
|
||||||
|
|
||||||
v9inode = V9FS_I(inode);
|
|
||||||
if (v9inode->cache_validity & V9FS_INO_INVALID_ATTR) {
|
|
||||||
int retval;
|
|
||||||
struct v9fs_session_info *v9ses;
|
|
||||||
fid = v9fs_fid_lookup(dentry);
|
|
||||||
if (IS_ERR(fid))
|
|
||||||
return PTR_ERR(fid);
|
|
||||||
|
|
||||||
v9ses = v9fs_inode2v9ses(inode);
|
|
||||||
if (v9fs_proto_dotl(v9ses))
|
|
||||||
retval = v9fs_refresh_inode_dotl(fid, inode);
|
|
||||||
else
|
|
||||||
retval = v9fs_refresh_inode(fid, inode);
|
|
||||||
if (retval == -ENOENT)
|
|
||||||
return 0;
|
|
||||||
if (retval < 0)
|
|
||||||
return retval;
|
|
||||||
}
|
|
||||||
out_valid:
|
|
||||||
return 1;
|
|
||||||
}
|
|
||||||
|
|
||||||
const struct dentry_operations v9fs_cached_dentry_operations = {
|
|
||||||
.d_revalidate = v9fs_lookup_revalidate,
|
|
||||||
.d_weak_revalidate = v9fs_lookup_revalidate,
|
|
||||||
.d_delete = v9fs_cached_dentry_delete,
|
|
||||||
.d_release = v9fs_dentry_release,
|
|
||||||
};
|
|
||||||
|
|
||||||
const struct dentry_operations v9fs_dentry_operations = {
|
|
||||||
.d_delete = v9fs_dentry_delete,
|
|
||||||
.d_release = v9fs_dentry_release,
|
|
||||||
};
|
|
@ -1,269 +0,0 @@
|
|||||||
/*
|
|
||||||
* linux/fs/9p/vfs_dir.c
|
|
||||||
*
|
|
||||||
* This file contains vfs directory ops for the 9P2000 protocol.
|
|
||||||
*
|
|
||||||
* Copyright (C) 2004 by Eric Van Hensbergen <ericvh@gmail.com>
|
|
||||||
* Copyright (C) 2002 by Ron Minnich <rminnich@lanl.gov>
|
|
||||||
*
|
|
||||||
* This program is free software; you can redistribute it and/or modify
|
|
||||||
* it under the terms of the GNU General Public License version 2
|
|
||||||
* as published by the Free Software Foundation.
|
|
||||||
*
|
|
||||||
* This program is distributed in the hope that it will be useful,
|
|
||||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
||||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
||||||
* GNU General Public License for more details.
|
|
||||||
*
|
|
||||||
* You should have received a copy of the GNU General Public License
|
|
||||||
* along with this program; if not, write to:
|
|
||||||
* Free Software Foundation
|
|
||||||
* 51 Franklin Street, Fifth Floor
|
|
||||||
* Boston, MA 02111-1301 USA
|
|
||||||
*
|
|
||||||
*/
|
|
||||||
|
|
||||||
#include <linux/module.h>
|
|
||||||
#include <linux/errno.h>
|
|
||||||
#include <linux/fs.h>
|
|
||||||
#include <linux/file.h>
|
|
||||||
#include <linux/stat.h>
|
|
||||||
#include <linux/string.h>
|
|
||||||
#include <linux/sched.h>
|
|
||||||
#include <linux/inet.h>
|
|
||||||
#include <linux/idr.h>
|
|
||||||
#include <linux/slab.h>
|
|
||||||
#include <net/9p/9p.h>
|
|
||||||
#include <net/9p/client.h>
|
|
||||||
|
|
||||||
#include "v9fs.h"
|
|
||||||
#include "v9fs_vfs.h"
|
|
||||||
#include "fid.h"
|
|
||||||
|
|
||||||
/**
|
|
||||||
* struct p9_rdir - readdir accounting
|
|
||||||
* @mutex: mutex protecting readdir
|
|
||||||
* @head: start offset of current dirread buffer
|
|
||||||
* @tail: end offset of current dirread buffer
|
|
||||||
* @buf: dirread buffer
|
|
||||||
*
|
|
||||||
* private structure for keeping track of readdir
|
|
||||||
* allocated on demand
|
|
||||||
*/
|
|
||||||
|
|
||||||
struct p9_rdir {
|
|
||||||
int head;
|
|
||||||
int tail;
|
|
||||||
uint8_t buf[];
|
|
||||||
};
|
|
||||||
|
|
||||||
/**
|
|
||||||
* dt_type - return file type
|
|
||||||
* @mistat: mistat structure
|
|
||||||
*
|
|
||||||
*/
|
|
||||||
|
|
||||||
static inline int dt_type(struct p9_wstat *mistat)
|
|
||||||
{
|
|
||||||
unsigned long perm = mistat->mode;
|
|
||||||
int rettype = DT_REG;
|
|
||||||
|
|
||||||
if (perm & P9_DMDIR)
|
|
||||||
rettype = DT_DIR;
|
|
||||||
if (perm & P9_DMSYMLINK)
|
|
||||||
rettype = DT_LNK;
|
|
||||||
|
|
||||||
return rettype;
|
|
||||||
}
|
|
||||||
|
|
||||||
static void p9stat_init(struct p9_wstat *stbuf)
|
|
||||||
{
|
|
||||||
stbuf->name = NULL;
|
|
||||||
stbuf->uid = NULL;
|
|
||||||
stbuf->gid = NULL;
|
|
||||||
stbuf->muid = NULL;
|
|
||||||
stbuf->extension = NULL;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* v9fs_alloc_rdir_buf - Allocate buffer used for read and readdir
|
|
||||||
* @filp: opened file structure
|
|
||||||
* @buflen: Length in bytes of buffer to allocate
|
|
||||||
*
|
|
||||||
*/
|
|
||||||
|
|
||||||
static struct p9_rdir *v9fs_alloc_rdir_buf(struct file *filp, int buflen)
|
|
||||||
{
|
|
||||||
struct p9_fid *fid = filp->private_data;
|
|
||||||
if (!fid->rdir)
|
|
||||||
fid->rdir = kzalloc(sizeof(struct p9_rdir) + buflen, GFP_KERNEL);
|
|
||||||
return fid->rdir;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* v9fs_dir_readdir - read a directory
|
|
||||||
* @filp: opened file structure
|
|
||||||
* @dirent: directory structure ???
|
|
||||||
* @filldir: function to populate directory structure ???
|
|
||||||
*
|
|
||||||
*/
|
|
||||||
|
|
||||||
static int v9fs_dir_readdir(struct file *filp, void *dirent, filldir_t filldir)
|
|
||||||
{
|
|
||||||
int over;
|
|
||||||
struct p9_wstat st;
|
|
||||||
int err = 0;
|
|
||||||
struct p9_fid *fid;
|
|
||||||
int buflen;
|
|
||||||
int reclen = 0;
|
|
||||||
struct p9_rdir *rdir;
|
|
||||||
|
|
||||||
p9_debug(P9_DEBUG_VFS, "name %s\n", filp->f_path.dentry->d_name.name);
|
|
||||||
fid = filp->private_data;
|
|
||||||
|
|
||||||
buflen = fid->clnt->msize - P9_IOHDRSZ;
|
|
||||||
|
|
||||||
rdir = v9fs_alloc_rdir_buf(filp, buflen);
|
|
||||||
if (!rdir)
|
|
||||||
return -ENOMEM;
|
|
||||||
|
|
||||||
while (1) {
|
|
||||||
if (rdir->tail == rdir->head) {
|
|
||||||
err = v9fs_file_readn(filp, rdir->buf, NULL,
|
|
||||||
buflen, filp->f_pos);
|
|
||||||
if (err <= 0)
|
|
||||||
return err;
|
|
||||||
|
|
||||||
rdir->head = 0;
|
|
||||||
rdir->tail = err;
|
|
||||||
}
|
|
||||||
while (rdir->head < rdir->tail) {
|
|
||||||
p9stat_init(&st);
|
|
||||||
err = p9stat_read(fid->clnt, rdir->buf + rdir->head,
|
|
||||||
rdir->tail - rdir->head, &st);
|
|
||||||
if (err) {
|
|
||||||
p9_debug(P9_DEBUG_VFS, "returned %d\n", err);
|
|
||||||
p9stat_free(&st);
|
|
||||||
return -EIO;
|
|
||||||
}
|
|
||||||
reclen = st.size+2;
|
|
||||||
|
|
||||||
over = filldir(dirent, st.name, strlen(st.name),
|
|
||||||
filp->f_pos, v9fs_qid2ino(&st.qid), dt_type(&st));
|
|
||||||
|
|
||||||
p9stat_free(&st);
|
|
||||||
|
|
||||||
if (over)
|
|
||||||
return 0;
|
|
||||||
|
|
||||||
rdir->head += reclen;
|
|
||||||
filp->f_pos += reclen;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* v9fs_dir_readdir_dotl - read a directory
|
|
||||||
* @filp: opened file structure
|
|
||||||
* @dirent: buffer to fill dirent structures
|
|
||||||
* @filldir: function to populate dirent structures
|
|
||||||
*
|
|
||||||
*/
|
|
||||||
static int v9fs_dir_readdir_dotl(struct file *filp, void *dirent,
|
|
||||||
filldir_t filldir)
|
|
||||||
{
|
|
||||||
int over;
|
|
||||||
int err = 0;
|
|
||||||
struct p9_fid *fid;
|
|
||||||
int buflen;
|
|
||||||
struct p9_rdir *rdir;
|
|
||||||
struct p9_dirent curdirent;
|
|
||||||
u64 oldoffset = 0;
|
|
||||||
|
|
||||||
p9_debug(P9_DEBUG_VFS, "name %s\n", filp->f_path.dentry->d_name.name);
|
|
||||||
fid = filp->private_data;
|
|
||||||
|
|
||||||
buflen = fid->clnt->msize - P9_READDIRHDRSZ;
|
|
||||||
|
|
||||||
rdir = v9fs_alloc_rdir_buf(filp, buflen);
|
|
||||||
if (!rdir)
|
|
||||||
return -ENOMEM;
|
|
||||||
|
|
||||||
while (1) {
|
|
||||||
if (rdir->tail == rdir->head) {
|
|
||||||
err = p9_client_readdir(fid, rdir->buf, buflen,
|
|
||||||
filp->f_pos);
|
|
||||||
if (err <= 0)
|
|
||||||
return err;
|
|
||||||
|
|
||||||
rdir->head = 0;
|
|
||||||
rdir->tail = err;
|
|
||||||
}
|
|
||||||
|
|
||||||
while (rdir->head < rdir->tail) {
|
|
||||||
|
|
||||||
err = p9dirent_read(fid->clnt, rdir->buf + rdir->head,
|
|
||||||
rdir->tail - rdir->head,
|
|
||||||
&curdirent);
|
|
||||||
if (err < 0) {
|
|
||||||
p9_debug(P9_DEBUG_VFS, "returned %d\n", err);
|
|
||||||
return -EIO;
|
|
||||||
}
|
|
||||||
|
|
||||||
/* d_off in dirent structure tracks the offset into
|
|
||||||
* the next dirent in the dir. However, filldir()
|
|
||||||
* expects offset into the current dirent. Hence
|
|
||||||
* while calling filldir send the offset from the
|
|
||||||
* previous dirent structure.
|
|
||||||
*/
|
|
||||||
over = filldir(dirent, curdirent.d_name,
|
|
||||||
strlen(curdirent.d_name),
|
|
||||||
oldoffset, v9fs_qid2ino(&curdirent.qid),
|
|
||||||
curdirent.d_type);
|
|
||||||
oldoffset = curdirent.d_off;
|
|
||||||
|
|
||||||
if (over)
|
|
||||||
return 0;
|
|
||||||
|
|
||||||
filp->f_pos = curdirent.d_off;
|
|
||||||
rdir->head += err;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
/**
|
|
||||||
* v9fs_dir_release - close a directory
|
|
||||||
* @inode: inode of the directory
|
|
||||||
* @filp: file pointer to a directory
|
|
||||||
*
|
|
||||||
*/
|
|
||||||
|
|
||||||
int v9fs_dir_release(struct inode *inode, struct file *filp)
|
|
||||||
{
|
|
||||||
struct p9_fid *fid;
|
|
||||||
|
|
||||||
fid = filp->private_data;
|
|
||||||
p9_debug(P9_DEBUG_VFS, "inode: %p filp: %p fid: %d\n",
|
|
||||||
inode, filp, fid ? fid->fid : -1);
|
|
||||||
if (fid)
|
|
||||||
p9_client_clunk(fid);
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
const struct file_operations v9fs_dir_operations = {
|
|
||||||
.read = generic_read_dir,
|
|
||||||
.llseek = generic_file_llseek,
|
|
||||||
.readdir = v9fs_dir_readdir,
|
|
||||||
.open = v9fs_file_open,
|
|
||||||
.release = v9fs_dir_release,
|
|
||||||
};
|
|
||||||
|
|
||||||
const struct file_operations v9fs_dir_operations_dotl = {
|
|
||||||
.read = generic_read_dir,
|
|
||||||
.llseek = generic_file_llseek,
|
|
||||||
.readdir = v9fs_dir_readdir_dotl,
|
|
||||||
.open = v9fs_file_open,
|
|
||||||
.release = v9fs_dir_release,
|
|
||||||
.fsync = v9fs_file_fsync_dotl,
|
|
||||||
};
|
|
@ -1,790 +0,0 @@
|
|||||||
/*
|
|
||||||
* linux/fs/9p/vfs_file.c
|
|
||||||
*
|
|
||||||
* This file contians vfs file ops for 9P2000.
|
|
||||||
*
|
|
||||||
* Copyright (C) 2004 by Eric Van Hensbergen <ericvh@gmail.com>
|
|
||||||
* Copyright (C) 2002 by Ron Minnich <rminnich@lanl.gov>
|
|
||||||
*
|
|
||||||
* This program is free software; you can redistribute it and/or modify
|
|
||||||
* it under the terms of the GNU General Public License version 2
|
|
||||||
* as published by the Free Software Foundation.
|
|
||||||
*
|
|
||||||
* This program is distributed in the hope that it will be useful,
|
|
||||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
||||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
||||||
* GNU General Public License for more details.
|
|
||||||
*
|
|
||||||
* You should have received a copy of the GNU General Public License
|
|
||||||
* along with this program; if not, write to:
|
|
||||||
* Free Software Foundation
|
|
||||||
* 51 Franklin Street, Fifth Floor
|
|
||||||
* Boston, MA 02111-1301 USA
|
|
||||||
*
|
|
||||||
*/
|
|
||||||
|
|
||||||
#include <linux/module.h>
|
|
||||||
#include <linux/errno.h>
|
|
||||||
#include <linux/fs.h>
|
|
||||||
#include <linux/sched.h>
|
|
||||||
#include <linux/file.h>
|
|
||||||
#include <linux/stat.h>
|
|
||||||
#include <linux/string.h>
|
|
||||||
#include <linux/inet.h>
|
|
||||||
#include <linux/list.h>
|
|
||||||
#include <linux/pagemap.h>
|
|
||||||
#include <linux/utsname.h>
|
|
||||||
#include <asm/uaccess.h>
|
|
||||||
#include <linux/idr.h>
|
|
||||||
#include <net/9p/9p.h>
|
|
||||||
#include <net/9p/client.h>
|
|
||||||
|
|
||||||
#include "v9fs.h"
|
|
||||||
#include "v9fs_vfs.h"
|
|
||||||
#include "fid.h"
|
|
||||||
#include "cache.h"
|
|
||||||
|
|
||||||
static const struct vm_operations_struct v9fs_file_vm_ops;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* v9fs_file_open - open a file (or directory)
|
|
||||||
* @inode: inode to be opened
|
|
||||||
* @file: file being opened
|
|
||||||
*
|
|
||||||
*/
|
|
||||||
|
|
||||||
int v9fs_file_open(struct inode *inode, struct file *file)
|
|
||||||
{
|
|
||||||
int err;
|
|
||||||
struct v9fs_inode *v9inode;
|
|
||||||
struct v9fs_session_info *v9ses;
|
|
||||||
struct p9_fid *fid;
|
|
||||||
int omode;
|
|
||||||
|
|
||||||
p9_debug(P9_DEBUG_VFS, "inode: %p file: %p\n", inode, file);
|
|
||||||
v9inode = V9FS_I(inode);
|
|
||||||
v9ses = v9fs_inode2v9ses(inode);
|
|
||||||
if (v9fs_proto_dotl(v9ses))
|
|
||||||
omode = v9fs_open_to_dotl_flags(file->f_flags);
|
|
||||||
else
|
|
||||||
omode = v9fs_uflags2omode(file->f_flags,
|
|
||||||
v9fs_proto_dotu(v9ses));
|
|
||||||
fid = file->private_data;
|
|
||||||
if (!fid) {
|
|
||||||
fid = v9fs_fid_clone(file->f_path.dentry);
|
|
||||||
if (IS_ERR(fid))
|
|
||||||
return PTR_ERR(fid);
|
|
||||||
|
|
||||||
err = p9_client_open(fid, omode);
|
|
||||||
if (err < 0) {
|
|
||||||
p9_client_clunk(fid);
|
|
||||||
return err;
|
|
||||||
}
|
|
||||||
if ((file->f_flags & O_APPEND) &&
|
|
||||||
(!v9fs_proto_dotu(v9ses) && !v9fs_proto_dotl(v9ses)))
|
|
||||||
generic_file_llseek(file, 0, SEEK_END);
|
|
||||||
}
|
|
||||||
|
|
||||||
file->private_data = fid;
|
|
||||||
mutex_lock(&v9inode->v_mutex);
|
|
||||||
if (v9ses->cache && !v9inode->writeback_fid &&
|
|
||||||
((file->f_flags & O_ACCMODE) != O_RDONLY)) {
|
|
||||||
/*
|
|
||||||
* clone a fid and add it to writeback_fid
|
|
||||||
* we do it during open time instead of
|
|
||||||
* page dirty time via write_begin/page_mkwrite
|
|
||||||
* because we want write after unlink usecase
|
|
||||||
* to work.
|
|
||||||
*/
|
|
||||||
fid = v9fs_writeback_fid(file->f_path.dentry);
|
|
||||||
if (IS_ERR(fid)) {
|
|
||||||
err = PTR_ERR(fid);
|
|
||||||
mutex_unlock(&v9inode->v_mutex);
|
|
||||||
goto out_error;
|
|
||||||
}
|
|
||||||
v9inode->writeback_fid = (void *) fid;
|
|
||||||
}
|
|
||||||
mutex_unlock(&v9inode->v_mutex);
|
|
||||||
#ifdef CONFIG_9P_FSCACHE
|
|
||||||
if (v9ses->cache)
|
|
||||||
v9fs_cache_inode_set_cookie(inode, file);
|
|
||||||
#endif
|
|
||||||
return 0;
|
|
||||||
out_error:
|
|
||||||
p9_client_clunk(file->private_data);
|
|
||||||
file->private_data = NULL;
|
|
||||||
return err;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* v9fs_file_lock - lock a file (or directory)
|
|
||||||
* @filp: file to be locked
|
|
||||||
* @cmd: lock command
|
|
||||||
* @fl: file lock structure
|
|
||||||
*
|
|
||||||
* Bugs: this looks like a local only lock, we should extend into 9P
|
|
||||||
* by using open exclusive
|
|
||||||
*/
|
|
||||||
|
|
||||||
static int v9fs_file_lock(struct file *filp, int cmd, struct file_lock *fl)
|
|
||||||
{
|
|
||||||
int res = 0;
|
|
||||||
struct inode *inode = file_inode(filp);
|
|
||||||
|
|
||||||
p9_debug(P9_DEBUG_VFS, "filp: %p lock: %p\n", filp, fl);
|
|
||||||
|
|
||||||
/* No mandatory locks */
|
|
||||||
if (__mandatory_lock(inode) && fl->fl_type != F_UNLCK)
|
|
||||||
return -ENOLCK;
|
|
||||||
|
|
||||||
if ((IS_SETLK(cmd) || IS_SETLKW(cmd)) && fl->fl_type != F_UNLCK) {
|
|
||||||
filemap_write_and_wait(inode->i_mapping);
|
|
||||||
invalidate_mapping_pages(&inode->i_data, 0, -1);
|
|
||||||
}
|
|
||||||
|
|
||||||
return res;
|
|
||||||
}
|
|
||||||
|
|
||||||
static int v9fs_file_do_lock(struct file *filp, int cmd, struct file_lock *fl)
|
|
||||||
{
|
|
||||||
struct p9_flock flock;
|
|
||||||
struct p9_fid *fid;
|
|
||||||
uint8_t status;
|
|
||||||
int res = 0;
|
|
||||||
unsigned char fl_type;
|
|
||||||
|
|
||||||
fid = filp->private_data;
|
|
||||||
BUG_ON(fid == NULL);
|
|
||||||
|
|
||||||
if ((fl->fl_flags & FL_POSIX) != FL_POSIX)
|
|
||||||
BUG();
|
|
||||||
|
|
||||||
res = posix_lock_file_wait(filp, fl);
|
|
||||||
if (res < 0)
|
|
||||||
goto out;
|
|
||||||
|
|
||||||
/* convert posix lock to p9 tlock args */
|
|
||||||
memset(&flock, 0, sizeof(flock));
|
|
||||||
/* map the lock type */
|
|
||||||
switch (fl->fl_type) {
|
|
||||||
case F_RDLCK:
|
|
||||||
flock.type = P9_LOCK_TYPE_RDLCK;
|
|
||||||
break;
|
|
||||||
case F_WRLCK:
|
|
||||||
flock.type = P9_LOCK_TYPE_WRLCK;
|
|
||||||
break;
|
|
||||||
case F_UNLCK:
|
|
||||||
flock.type = P9_LOCK_TYPE_UNLCK;
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
flock.start = fl->fl_start;
|
|
||||||
if (fl->fl_end == OFFSET_MAX)
|
|
||||||
flock.length = 0;
|
|
||||||
else
|
|
||||||
flock.length = fl->fl_end - fl->fl_start + 1;
|
|
||||||
flock.proc_id = fl->fl_pid;
|
|
||||||
flock.client_id = utsname()->nodename;
|
|
||||||
if (IS_SETLKW(cmd))
|
|
||||||
flock.flags = P9_LOCK_FLAGS_BLOCK;
|
|
||||||
|
|
||||||
/*
|
|
||||||
* if its a blocked request and we get P9_LOCK_BLOCKED as the status
|
|
||||||
* for lock request, keep on trying
|
|
||||||
*/
|
|
||||||
for (;;) {
|
|
||||||
res = p9_client_lock_dotl(fid, &flock, &status);
|
|
||||||
if (res < 0)
|
|
||||||
break;
|
|
||||||
|
|
||||||
if (status != P9_LOCK_BLOCKED)
|
|
||||||
break;
|
|
||||||
if (status == P9_LOCK_BLOCKED && !IS_SETLKW(cmd))
|
|
||||||
break;
|
|
||||||
if (schedule_timeout_interruptible(P9_LOCK_TIMEOUT) != 0)
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
|
|
||||||
/* map 9p status to VFS status */
|
|
||||||
switch (status) {
|
|
||||||
case P9_LOCK_SUCCESS:
|
|
||||||
res = 0;
|
|
||||||
break;
|
|
||||||
case P9_LOCK_BLOCKED:
|
|
||||||
res = -EAGAIN;
|
|
||||||
break;
|
|
||||||
case P9_LOCK_ERROR:
|
|
||||||
case P9_LOCK_GRACE:
|
|
||||||
res = -ENOLCK;
|
|
||||||
break;
|
|
||||||
default:
|
|
||||||
BUG();
|
|
||||||
}
|
|
||||||
|
|
||||||
/*
|
|
||||||
* incase server returned error for lock request, revert
|
|
||||||
* it locally
|
|
||||||
*/
|
|
||||||
if (res < 0 && fl->fl_type != F_UNLCK) {
|
|
||||||
fl_type = fl->fl_type;
|
|
||||||
fl->fl_type = F_UNLCK;
|
|
||||||
res = posix_lock_file_wait(filp, fl);
|
|
||||||
fl->fl_type = fl_type;
|
|
||||||
}
|
|
||||||
out:
|
|
||||||
return res;
|
|
||||||
}
|
|
||||||
|
|
||||||
static int v9fs_file_getlock(struct file *filp, struct file_lock *fl)
|
|
||||||
{
|
|
||||||
struct p9_getlock glock;
|
|
||||||
struct p9_fid *fid;
|
|
||||||
int res = 0;
|
|
||||||
|
|
||||||
fid = filp->private_data;
|
|
||||||
BUG_ON(fid == NULL);
|
|
||||||
|
|
||||||
posix_test_lock(filp, fl);
|
|
||||||
/*
|
|
||||||
* if we have a conflicting lock locally, no need to validate
|
|
||||||
* with server
|
|
||||||
*/
|
|
||||||
if (fl->fl_type != F_UNLCK)
|
|
||||||
return res;
|
|
||||||
|
|
||||||
/* convert posix lock to p9 tgetlock args */
|
|
||||||
memset(&glock, 0, sizeof(glock));
|
|
||||||
glock.type = P9_LOCK_TYPE_UNLCK;
|
|
||||||
glock.start = fl->fl_start;
|
|
||||||
if (fl->fl_end == OFFSET_MAX)
|
|
||||||
glock.length = 0;
|
|
||||||
else
|
|
||||||
glock.length = fl->fl_end - fl->fl_start + 1;
|
|
||||||
glock.proc_id = fl->fl_pid;
|
|
||||||
glock.client_id = utsname()->nodename;
|
|
||||||
|
|
||||||
res = p9_client_getlock_dotl(fid, &glock);
|
|
||||||
if (res < 0)
|
|
||||||
return res;
|
|
||||||
/* map 9p lock type to os lock type */
|
|
||||||
switch (glock.type) {
|
|
||||||
case P9_LOCK_TYPE_RDLCK:
|
|
||||||
fl->fl_type = F_RDLCK;
|
|
||||||
break;
|
|
||||||
case P9_LOCK_TYPE_WRLCK:
|
|
||||||
fl->fl_type = F_WRLCK;
|
|
||||||
break;
|
|
||||||
case P9_LOCK_TYPE_UNLCK:
|
|
||||||
fl->fl_type = F_UNLCK;
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
if (glock.type != P9_LOCK_TYPE_UNLCK) {
|
|
||||||
fl->fl_start = glock.start;
|
|
||||||
if (glock.length == 0)
|
|
||||||
fl->fl_end = OFFSET_MAX;
|
|
||||||
else
|
|
||||||
fl->fl_end = glock.start + glock.length - 1;
|
|
||||||
fl->fl_pid = glock.proc_id;
|
|
||||||
}
|
|
||||||
return res;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* v9fs_file_lock_dotl - lock a file (or directory)
|
|
||||||
* @filp: file to be locked
|
|
||||||
* @cmd: lock command
|
|
||||||
* @fl: file lock structure
|
|
||||||
*
|
|
||||||
*/
|
|
||||||
|
|
||||||
static int v9fs_file_lock_dotl(struct file *filp, int cmd, struct file_lock *fl)
|
|
||||||
{
|
|
||||||
struct inode *inode = file_inode(filp);
|
|
||||||
int ret = -ENOLCK;
|
|
||||||
|
|
||||||
p9_debug(P9_DEBUG_VFS, "filp: %p cmd:%d lock: %p name: %s\n",
|
|
||||||
filp, cmd, fl, filp->f_path.dentry->d_name.name);
|
|
||||||
|
|
||||||
/* No mandatory locks */
|
|
||||||
if (__mandatory_lock(inode) && fl->fl_type != F_UNLCK)
|
|
||||||
goto out_err;
|
|
||||||
|
|
||||||
if ((IS_SETLK(cmd) || IS_SETLKW(cmd)) && fl->fl_type != F_UNLCK) {
|
|
||||||
filemap_write_and_wait(inode->i_mapping);
|
|
||||||
invalidate_mapping_pages(&inode->i_data, 0, -1);
|
|
||||||
}
|
|
||||||
|
|
||||||
if (IS_SETLK(cmd) || IS_SETLKW(cmd))
|
|
||||||
ret = v9fs_file_do_lock(filp, cmd, fl);
|
|
||||||
else if (IS_GETLK(cmd))
|
|
||||||
ret = v9fs_file_getlock(filp, fl);
|
|
||||||
else
|
|
||||||
ret = -EINVAL;
|
|
||||||
out_err:
|
|
||||||
return ret;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* v9fs_file_flock_dotl - lock a file
|
|
||||||
* @filp: file to be locked
|
|
||||||
* @cmd: lock command
|
|
||||||
* @fl: file lock structure
|
|
||||||
*
|
|
||||||
*/
|
|
||||||
|
|
||||||
static int v9fs_file_flock_dotl(struct file *filp, int cmd,
|
|
||||||
struct file_lock *fl)
|
|
||||||
{
|
|
||||||
struct inode *inode = file_inode(filp);
|
|
||||||
int ret = -ENOLCK;
|
|
||||||
|
|
||||||
p9_debug(P9_DEBUG_VFS, "filp: %p cmd:%d lock: %p name: %s\n",
|
|
||||||
filp, cmd, fl, filp->f_path.dentry->d_name.name);
|
|
||||||
|
|
||||||
/* No mandatory locks */
|
|
||||||
if (__mandatory_lock(inode) && fl->fl_type != F_UNLCK)
|
|
||||||
goto out_err;
|
|
||||||
|
|
||||||
if (!(fl->fl_flags & FL_FLOCK))
|
|
||||||
goto out_err;
|
|
||||||
|
|
||||||
if ((IS_SETLK(cmd) || IS_SETLKW(cmd)) && fl->fl_type != F_UNLCK) {
|
|
||||||
filemap_write_and_wait(inode->i_mapping);
|
|
||||||
invalidate_mapping_pages(&inode->i_data, 0, -1);
|
|
||||||
}
|
|
||||||
/* Convert flock to posix lock */
|
|
||||||
fl->fl_owner = (fl_owner_t)filp;
|
|
||||||
fl->fl_start = 0;
|
|
||||||
fl->fl_end = OFFSET_MAX;
|
|
||||||
fl->fl_flags |= FL_POSIX;
|
|
||||||
fl->fl_flags ^= FL_FLOCK;
|
|
||||||
|
|
||||||
if (IS_SETLK(cmd) | IS_SETLKW(cmd))
|
|
||||||
ret = v9fs_file_do_lock(filp, cmd, fl);
|
|
||||||
else
|
|
||||||
ret = -EINVAL;
|
|
||||||
out_err:
|
|
||||||
return ret;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* v9fs_fid_readn - read from a fid
|
|
||||||
* @fid: fid to read
|
|
||||||
* @data: data buffer to read data into
|
|
||||||
* @udata: user data buffer to read data into
|
|
||||||
* @count: size of buffer
|
|
||||||
* @offset: offset at which to read data
|
|
||||||
*
|
|
||||||
*/
|
|
||||||
ssize_t
|
|
||||||
v9fs_fid_readn(struct p9_fid *fid, char *data, char __user *udata, u32 count,
|
|
||||||
u64 offset)
|
|
||||||
{
|
|
||||||
int n, total, size;
|
|
||||||
|
|
||||||
p9_debug(P9_DEBUG_VFS, "fid %d offset %llu count %d\n",
|
|
||||||
fid->fid, (long long unsigned)offset, count);
|
|
||||||
n = 0;
|
|
||||||
total = 0;
|
|
||||||
size = fid->iounit ? fid->iounit : fid->clnt->msize - P9_IOHDRSZ;
|
|
||||||
do {
|
|
||||||
n = p9_client_read(fid, data, udata, offset, count);
|
|
||||||
if (n <= 0)
|
|
||||||
break;
|
|
||||||
|
|
||||||
if (data)
|
|
||||||
data += n;
|
|
||||||
if (udata)
|
|
||||||
udata += n;
|
|
||||||
|
|
||||||
offset += n;
|
|
||||||
count -= n;
|
|
||||||
total += n;
|
|
||||||
} while (count > 0 && n == size);
|
|
||||||
|
|
||||||
if (n < 0)
|
|
||||||
total = n;
|
|
||||||
|
|
||||||
return total;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* v9fs_file_readn - read from a file
|
|
||||||
* @filp: file pointer to read
|
|
||||||
* @data: data buffer to read data into
|
|
||||||
* @udata: user data buffer to read data into
|
|
||||||
* @count: size of buffer
|
|
||||||
* @offset: offset at which to read data
|
|
||||||
*
|
|
||||||
*/
|
|
||||||
ssize_t
|
|
||||||
v9fs_file_readn(struct file *filp, char *data, char __user *udata, u32 count,
|
|
||||||
u64 offset)
|
|
||||||
{
|
|
||||||
return v9fs_fid_readn(filp->private_data, data, udata, count, offset);
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* v9fs_file_read - read from a file
|
|
||||||
* @filp: file pointer to read
|
|
||||||
* @udata: user data buffer to read data into
|
|
||||||
* @count: size of buffer
|
|
||||||
* @offset: offset at which to read data
|
|
||||||
*
|
|
||||||
*/
|
|
||||||
|
|
||||||
static ssize_t
|
|
||||||
v9fs_file_read(struct file *filp, char __user *udata, size_t count,
|
|
||||||
loff_t * offset)
|
|
||||||
{
|
|
||||||
int ret;
|
|
||||||
struct p9_fid *fid;
|
|
||||||
size_t size;
|
|
||||||
|
|
||||||
p9_debug(P9_DEBUG_VFS, "count %zu offset %lld\n", count, *offset);
|
|
||||||
fid = filp->private_data;
|
|
||||||
|
|
||||||
size = fid->iounit ? fid->iounit : fid->clnt->msize - P9_IOHDRSZ;
|
|
||||||
if (count > size)
|
|
||||||
ret = v9fs_file_readn(filp, NULL, udata, count, *offset);
|
|
||||||
else
|
|
||||||
ret = p9_client_read(fid, NULL, udata, *offset, count);
|
|
||||||
|
|
||||||
if (ret > 0)
|
|
||||||
*offset += ret;
|
|
||||||
|
|
||||||
return ret;
|
|
||||||
}
|
|
||||||
|
|
||||||
ssize_t
|
|
||||||
v9fs_file_write_internal(struct inode *inode, struct p9_fid *fid,
|
|
||||||
const char __user *data, size_t count,
|
|
||||||
loff_t *offset, int invalidate)
|
|
||||||
{
|
|
||||||
int n;
|
|
||||||
loff_t i_size;
|
|
||||||
size_t total = 0;
|
|
||||||
struct p9_client *clnt;
|
|
||||||
loff_t origin = *offset;
|
|
||||||
unsigned long pg_start, pg_end;
|
|
||||||
|
|
||||||
p9_debug(P9_DEBUG_VFS, "data %p count %d offset %x\n",
|
|
||||||
data, (int)count, (int)*offset);
|
|
||||||
|
|
||||||
clnt = fid->clnt;
|
|
||||||
do {
|
|
||||||
n = p9_client_write(fid, NULL, data+total, origin+total, count);
|
|
||||||
if (n <= 0)
|
|
||||||
break;
|
|
||||||
count -= n;
|
|
||||||
total += n;
|
|
||||||
} while (count > 0);
|
|
||||||
|
|
||||||
if (invalidate && (total > 0)) {
|
|
||||||
pg_start = origin >> PAGE_CACHE_SHIFT;
|
|
||||||
pg_end = (origin + total - 1) >> PAGE_CACHE_SHIFT;
|
|
||||||
if (inode->i_mapping && inode->i_mapping->nrpages)
|
|
||||||
invalidate_inode_pages2_range(inode->i_mapping,
|
|
||||||
pg_start, pg_end);
|
|
||||||
*offset += total;
|
|
||||||
i_size = i_size_read(inode);
|
|
||||||
if (*offset > i_size) {
|
|
||||||
inode_add_bytes(inode, *offset - i_size);
|
|
||||||
i_size_write(inode, *offset);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if (n < 0)
|
|
||||||
return n;
|
|
||||||
|
|
||||||
return total;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* v9fs_file_write - write to a file
|
|
||||||
* @filp: file pointer to write
|
|
||||||
* @data: data buffer to write data from
|
|
||||||
* @count: size of buffer
|
|
||||||
* @offset: offset at which to write data
|
|
||||||
*
|
|
||||||
*/
|
|
||||||
static ssize_t
|
|
||||||
v9fs_file_write(struct file *filp, const char __user * data,
|
|
||||||
size_t count, loff_t *offset)
|
|
||||||
{
|
|
||||||
ssize_t retval = 0;
|
|
||||||
loff_t origin = *offset;
|
|
||||||
|
|
||||||
|
|
||||||
retval = generic_write_checks(filp, &origin, &count, 0);
|
|
||||||
if (retval)
|
|
||||||
goto out;
|
|
||||||
|
|
||||||
retval = -EINVAL;
|
|
||||||
if ((ssize_t) count < 0)
|
|
||||||
goto out;
|
|
||||||
retval = 0;
|
|
||||||
if (!count)
|
|
||||||
goto out;
|
|
||||||
|
|
||||||
retval = v9fs_file_write_internal(file_inode(filp),
|
|
||||||
filp->private_data,
|
|
||||||
data, count, &origin, 1);
|
|
||||||
/* update offset on successful write */
|
|
||||||
if (retval > 0)
|
|
||||||
*offset = origin;
|
|
||||||
out:
|
|
||||||
return retval;
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
static int v9fs_file_fsync(struct file *filp, loff_t start, loff_t end,
|
|
||||||
int datasync)
|
|
||||||
{
|
|
||||||
struct p9_fid *fid;
|
|
||||||
struct inode *inode = filp->f_mapping->host;
|
|
||||||
struct p9_wstat wstat;
|
|
||||||
int retval;
|
|
||||||
|
|
||||||
retval = filemap_write_and_wait_range(inode->i_mapping, start, end);
|
|
||||||
if (retval)
|
|
||||||
return retval;
|
|
||||||
|
|
||||||
mutex_lock(&inode->i_mutex);
|
|
||||||
p9_debug(P9_DEBUG_VFS, "filp %p datasync %x\n", filp, datasync);
|
|
||||||
|
|
||||||
fid = filp->private_data;
|
|
||||||
v9fs_blank_wstat(&wstat);
|
|
||||||
|
|
||||||
retval = p9_client_wstat(fid, &wstat);
|
|
||||||
mutex_unlock(&inode->i_mutex);
|
|
||||||
|
|
||||||
return retval;
|
|
||||||
}
|
|
||||||
|
|
||||||
int v9fs_file_fsync_dotl(struct file *filp, loff_t start, loff_t end,
|
|
||||||
int datasync)
|
|
||||||
{
|
|
||||||
struct p9_fid *fid;
|
|
||||||
struct inode *inode = filp->f_mapping->host;
|
|
||||||
int retval;
|
|
||||||
|
|
||||||
retval = filemap_write_and_wait_range(inode->i_mapping, start, end);
|
|
||||||
if (retval)
|
|
||||||
return retval;
|
|
||||||
|
|
||||||
mutex_lock(&inode->i_mutex);
|
|
||||||
p9_debug(P9_DEBUG_VFS, "filp %p datasync %x\n", filp, datasync);
|
|
||||||
|
|
||||||
fid = filp->private_data;
|
|
||||||
|
|
||||||
retval = p9_client_fsync(fid, datasync);
|
|
||||||
mutex_unlock(&inode->i_mutex);
|
|
||||||
|
|
||||||
return retval;
|
|
||||||
}
|
|
||||||
|
|
||||||
static int
|
|
||||||
v9fs_file_mmap(struct file *file, struct vm_area_struct *vma)
|
|
||||||
{
|
|
||||||
int retval;
|
|
||||||
|
|
||||||
retval = generic_file_mmap(file, vma);
|
|
||||||
if (!retval)
|
|
||||||
vma->vm_ops = &v9fs_file_vm_ops;
|
|
||||||
|
|
||||||
return retval;
|
|
||||||
}
|
|
||||||
|
|
||||||
static int
|
|
||||||
v9fs_vm_page_mkwrite(struct vm_area_struct *vma, struct vm_fault *vmf)
|
|
||||||
{
|
|
||||||
struct v9fs_inode *v9inode;
|
|
||||||
struct page *page = vmf->page;
|
|
||||||
struct file *filp = vma->vm_file;
|
|
||||||
struct inode *inode = file_inode(filp);
|
|
||||||
|
|
||||||
|
|
||||||
p9_debug(P9_DEBUG_VFS, "page %p fid %lx\n",
|
|
||||||
page, (unsigned long)filp->private_data);
|
|
||||||
|
|
||||||
/* Update file times before taking page lock */
|
|
||||||
file_update_time(filp);
|
|
||||||
|
|
||||||
v9inode = V9FS_I(inode);
|
|
||||||
/* make sure the cache has finished storing the page */
|
|
||||||
v9fs_fscache_wait_on_page_write(inode, page);
|
|
||||||
BUG_ON(!v9inode->writeback_fid);
|
|
||||||
lock_page(page);
|
|
||||||
if (page->mapping != inode->i_mapping)
|
|
||||||
goto out_unlock;
|
|
||||||
wait_for_stable_page(page);
|
|
||||||
|
|
||||||
return VM_FAULT_LOCKED;
|
|
||||||
out_unlock:
|
|
||||||
unlock_page(page);
|
|
||||||
return VM_FAULT_NOPAGE;
|
|
||||||
}
|
|
||||||
|
|
||||||
static ssize_t
|
|
||||||
v9fs_direct_read(struct file *filp, char __user *udata, size_t count,
|
|
||||||
loff_t *offsetp)
|
|
||||||
{
|
|
||||||
loff_t size, offset;
|
|
||||||
struct inode *inode;
|
|
||||||
struct address_space *mapping;
|
|
||||||
|
|
||||||
offset = *offsetp;
|
|
||||||
mapping = filp->f_mapping;
|
|
||||||
inode = mapping->host;
|
|
||||||
if (!count)
|
|
||||||
return 0;
|
|
||||||
size = i_size_read(inode);
|
|
||||||
if (offset < size)
|
|
||||||
filemap_write_and_wait_range(mapping, offset,
|
|
||||||
offset + count - 1);
|
|
||||||
|
|
||||||
return v9fs_file_read(filp, udata, count, offsetp);
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* v9fs_cached_file_read - read from a file
|
|
||||||
* @filp: file pointer to read
|
|
||||||
* @udata: user data buffer to read data into
|
|
||||||
* @count: size of buffer
|
|
||||||
* @offset: offset at which to read data
|
|
||||||
*
|
|
||||||
*/
|
|
||||||
static ssize_t
|
|
||||||
v9fs_cached_file_read(struct file *filp, char __user *data, size_t count,
|
|
||||||
loff_t *offset)
|
|
||||||
{
|
|
||||||
if (filp->f_flags & O_DIRECT)
|
|
||||||
return v9fs_direct_read(filp, data, count, offset);
|
|
||||||
return do_sync_read(filp, data, count, offset);
|
|
||||||
}
|
|
||||||
|
|
||||||
static ssize_t
|
|
||||||
v9fs_direct_write(struct file *filp, const char __user * data,
|
|
||||||
size_t count, loff_t *offsetp)
|
|
||||||
{
|
|
||||||
loff_t offset;
|
|
||||||
ssize_t retval;
|
|
||||||
struct inode *inode;
|
|
||||||
struct address_space *mapping;
|
|
||||||
|
|
||||||
offset = *offsetp;
|
|
||||||
mapping = filp->f_mapping;
|
|
||||||
inode = mapping->host;
|
|
||||||
if (!count)
|
|
||||||
return 0;
|
|
||||||
|
|
||||||
mutex_lock(&inode->i_mutex);
|
|
||||||
retval = filemap_write_and_wait_range(mapping, offset,
|
|
||||||
offset + count - 1);
|
|
||||||
if (retval)
|
|
||||||
goto err_out;
|
|
||||||
/*
|
|
||||||
* After a write we want buffered reads to be sure to go to disk to get
|
|
||||||
* the new data. We invalidate clean cached page from the region we're
|
|
||||||
* about to write. We do this *before* the write so that if we fail
|
|
||||||
* here we fall back to buffered write
|
|
||||||
*/
|
|
||||||
if (mapping->nrpages) {
|
|
||||||
pgoff_t pg_start = offset >> PAGE_CACHE_SHIFT;
|
|
||||||
pgoff_t pg_end = (offset + count - 1) >> PAGE_CACHE_SHIFT;
|
|
||||||
|
|
||||||
retval = invalidate_inode_pages2_range(mapping,
|
|
||||||
pg_start, pg_end);
|
|
||||||
/*
|
|
||||||
* If a page can not be invalidated, fall back
|
|
||||||
* to buffered write.
|
|
||||||
*/
|
|
||||||
if (retval) {
|
|
||||||
if (retval == -EBUSY)
|
|
||||||
goto buff_write;
|
|
||||||
goto err_out;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
retval = v9fs_file_write(filp, data, count, offsetp);
|
|
||||||
err_out:
|
|
||||||
mutex_unlock(&inode->i_mutex);
|
|
||||||
return retval;
|
|
||||||
|
|
||||||
buff_write:
|
|
||||||
mutex_unlock(&inode->i_mutex);
|
|
||||||
return do_sync_write(filp, data, count, offsetp);
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* v9fs_cached_file_write - write to a file
|
|
||||||
* @filp: file pointer to write
|
|
||||||
* @data: data buffer to write data from
|
|
||||||
* @count: size of buffer
|
|
||||||
* @offset: offset at which to write data
|
|
||||||
*
|
|
||||||
*/
|
|
||||||
static ssize_t
|
|
||||||
v9fs_cached_file_write(struct file *filp, const char __user * data,
|
|
||||||
size_t count, loff_t *offset)
|
|
||||||
{
|
|
||||||
|
|
||||||
if (filp->f_flags & O_DIRECT)
|
|
||||||
return v9fs_direct_write(filp, data, count, offset);
|
|
||||||
return do_sync_write(filp, data, count, offset);
|
|
||||||
}
|
|
||||||
|
|
||||||
static const struct vm_operations_struct v9fs_file_vm_ops = {
|
|
||||||
.fault = filemap_fault,
|
|
||||||
.page_mkwrite = v9fs_vm_page_mkwrite,
|
|
||||||
.remap_pages = generic_file_remap_pages,
|
|
||||||
};
|
|
||||||
|
|
||||||
|
|
||||||
const struct file_operations v9fs_cached_file_operations = {
|
|
||||||
.llseek = generic_file_llseek,
|
|
||||||
.read = v9fs_cached_file_read,
|
|
||||||
.write = v9fs_cached_file_write,
|
|
||||||
.aio_read = generic_file_aio_read,
|
|
||||||
.aio_write = generic_file_aio_write,
|
|
||||||
.open = v9fs_file_open,
|
|
||||||
.release = v9fs_dir_release,
|
|
||||||
.lock = v9fs_file_lock,
|
|
||||||
.mmap = v9fs_file_mmap,
|
|
||||||
.fsync = v9fs_file_fsync,
|
|
||||||
};
|
|
||||||
|
|
||||||
const struct file_operations v9fs_cached_file_operations_dotl = {
|
|
||||||
.llseek = generic_file_llseek,
|
|
||||||
.read = v9fs_cached_file_read,
|
|
||||||
.write = v9fs_cached_file_write,
|
|
||||||
.aio_read = generic_file_aio_read,
|
|
||||||
.aio_write = generic_file_aio_write,
|
|
||||||
.open = v9fs_file_open,
|
|
||||||
.release = v9fs_dir_release,
|
|
||||||
.lock = v9fs_file_lock_dotl,
|
|
||||||
.flock = v9fs_file_flock_dotl,
|
|
||||||
.mmap = v9fs_file_mmap,
|
|
||||||
.fsync = v9fs_file_fsync_dotl,
|
|
||||||
};
|
|
||||||
|
|
||||||
const struct file_operations v9fs_file_operations = {
|
|
||||||
.llseek = generic_file_llseek,
|
|
||||||
.read = v9fs_file_read,
|
|
||||||
.write = v9fs_file_write,
|
|
||||||
.open = v9fs_file_open,
|
|
||||||
.release = v9fs_dir_release,
|
|
||||||
.lock = v9fs_file_lock,
|
|
||||||
.mmap = generic_file_readonly_mmap,
|
|
||||||
.fsync = v9fs_file_fsync,
|
|
||||||
};
|
|
||||||
|
|
||||||
const struct file_operations v9fs_file_operations_dotl = {
|
|
||||||
.llseek = generic_file_llseek,
|
|
||||||
.read = v9fs_file_read,
|
|
||||||
.write = v9fs_file_write,
|
|
||||||
.open = v9fs_file_open,
|
|
||||||
.release = v9fs_dir_release,
|
|
||||||
.lock = v9fs_file_lock_dotl,
|
|
||||||
.flock = v9fs_file_flock_dotl,
|
|
||||||
.mmap = generic_file_readonly_mmap,
|
|
||||||
.fsync = v9fs_file_fsync_dotl,
|
|
||||||
};
|
|
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
@ -1,368 +0,0 @@
|
|||||||
/*
|
|
||||||
* linux/fs/9p/vfs_super.c
|
|
||||||
*
|
|
||||||
* This file contians superblock ops for 9P2000. It is intended that
|
|
||||||
* you mount this file system on directories.
|
|
||||||
*
|
|
||||||
* Copyright (C) 2004 by Eric Van Hensbergen <ericvh@gmail.com>
|
|
||||||
* Copyright (C) 2002 by Ron Minnich <rminnich@lanl.gov>
|
|
||||||
*
|
|
||||||
* This program is free software; you can redistribute it and/or modify
|
|
||||||
* it under the terms of the GNU General Public License version 2
|
|
||||||
* as published by the Free Software Foundation.
|
|
||||||
*
|
|
||||||
* This program is distributed in the hope that it will be useful,
|
|
||||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
||||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
||||||
* GNU General Public License for more details.
|
|
||||||
*
|
|
||||||
* You should have received a copy of the GNU General Public License
|
|
||||||
* along with this program; if not, write to:
|
|
||||||
* Free Software Foundation
|
|
||||||
* 51 Franklin Street, Fifth Floor
|
|
||||||
* Boston, MA 02111-1301 USA
|
|
||||||
*
|
|
||||||
*/
|
|
||||||
|
|
||||||
#include <linux/kernel.h>
|
|
||||||
#include <linux/module.h>
|
|
||||||
#include <linux/errno.h>
|
|
||||||
#include <linux/fs.h>
|
|
||||||
#include <linux/file.h>
|
|
||||||
#include <linux/stat.h>
|
|
||||||
#include <linux/string.h>
|
|
||||||
#include <linux/inet.h>
|
|
||||||
#include <linux/pagemap.h>
|
|
||||||
#include <linux/seq_file.h>
|
|
||||||
#include <linux/mount.h>
|
|
||||||
#include <linux/idr.h>
|
|
||||||
#include <linux/sched.h>
|
|
||||||
#include <linux/slab.h>
|
|
||||||
#include <linux/statfs.h>
|
|
||||||
#include <linux/magic.h>
|
|
||||||
#include <net/9p/9p.h>
|
|
||||||
#include <net/9p/client.h>
|
|
||||||
|
|
||||||
#include "v9fs.h"
|
|
||||||
#include "v9fs_vfs.h"
|
|
||||||
#include "fid.h"
|
|
||||||
#include "xattr.h"
|
|
||||||
#include "acl.h"
|
|
||||||
|
|
||||||
static const struct super_operations v9fs_super_ops, v9fs_super_ops_dotl;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* v9fs_set_super - set the superblock
|
|
||||||
* @s: super block
|
|
||||||
* @data: file system specific data
|
|
||||||
*
|
|
||||||
*/
|
|
||||||
|
|
||||||
static int v9fs_set_super(struct super_block *s, void *data)
|
|
||||||
{
|
|
||||||
s->s_fs_info = data;
|
|
||||||
return set_anon_super(s, data);
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* v9fs_fill_super - populate superblock with info
|
|
||||||
* @sb: superblock
|
|
||||||
* @v9ses: session information
|
|
||||||
* @flags: flags propagated from v9fs_mount()
|
|
||||||
*
|
|
||||||
*/
|
|
||||||
|
|
||||||
static void
|
|
||||||
v9fs_fill_super(struct super_block *sb, struct v9fs_session_info *v9ses,
|
|
||||||
int flags, void *data)
|
|
||||||
{
|
|
||||||
sb->s_maxbytes = MAX_LFS_FILESIZE;
|
|
||||||
sb->s_blocksize_bits = fls(v9ses->maxdata - 1);
|
|
||||||
sb->s_blocksize = 1 << sb->s_blocksize_bits;
|
|
||||||
sb->s_magic = V9FS_MAGIC;
|
|
||||||
if (v9fs_proto_dotl(v9ses)) {
|
|
||||||
sb->s_op = &v9fs_super_ops_dotl;
|
|
||||||
sb->s_xattr = v9fs_xattr_handlers;
|
|
||||||
} else
|
|
||||||
sb->s_op = &v9fs_super_ops;
|
|
||||||
sb->s_bdi = &v9ses->bdi;
|
|
||||||
if (v9ses->cache)
|
|
||||||
sb->s_bdi->ra_pages = (VM_MAX_READAHEAD * 1024)/PAGE_CACHE_SIZE;
|
|
||||||
|
|
||||||
sb->s_flags |= MS_ACTIVE | MS_DIRSYNC | MS_NOATIME;
|
|
||||||
if (!v9ses->cache)
|
|
||||||
sb->s_flags |= MS_SYNCHRONOUS;
|
|
||||||
|
|
||||||
#ifdef CONFIG_9P_FS_POSIX_ACL
|
|
||||||
if ((v9ses->flags & V9FS_ACL_MASK) == V9FS_POSIX_ACL)
|
|
||||||
sb->s_flags |= MS_POSIXACL;
|
|
||||||
#endif
|
|
||||||
|
|
||||||
save_mount_options(sb, data);
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* v9fs_mount - mount a superblock
|
|
||||||
* @fs_type: file system type
|
|
||||||
* @flags: mount flags
|
|
||||||
* @dev_name: device name that was mounted
|
|
||||||
* @data: mount options
|
|
||||||
*
|
|
||||||
*/
|
|
||||||
|
|
||||||
static struct dentry *v9fs_mount(struct file_system_type *fs_type, int flags,
|
|
||||||
const char *dev_name, void *data)
|
|
||||||
{
|
|
||||||
struct super_block *sb = NULL;
|
|
||||||
struct inode *inode = NULL;
|
|
||||||
struct dentry *root = NULL;
|
|
||||||
struct v9fs_session_info *v9ses = NULL;
|
|
||||||
umode_t mode = S_IRWXUGO | S_ISVTX;
|
|
||||||
struct p9_fid *fid;
|
|
||||||
int retval = 0;
|
|
||||||
|
|
||||||
p9_debug(P9_DEBUG_VFS, "\n");
|
|
||||||
|
|
||||||
v9ses = kzalloc(sizeof(struct v9fs_session_info), GFP_KERNEL);
|
|
||||||
if (!v9ses)
|
|
||||||
return ERR_PTR(-ENOMEM);
|
|
||||||
|
|
||||||
fid = v9fs_session_init(v9ses, dev_name, data);
|
|
||||||
if (IS_ERR(fid)) {
|
|
||||||
retval = PTR_ERR(fid);
|
|
||||||
/*
|
|
||||||
* we need to call session_close to tear down some
|
|
||||||
* of the data structure setup by session_init
|
|
||||||
*/
|
|
||||||
goto close_session;
|
|
||||||
}
|
|
||||||
|
|
||||||
sb = sget(fs_type, NULL, v9fs_set_super, flags, v9ses);
|
|
||||||
if (IS_ERR(sb)) {
|
|
||||||
retval = PTR_ERR(sb);
|
|
||||||
goto clunk_fid;
|
|
||||||
}
|
|
||||||
v9fs_fill_super(sb, v9ses, flags, data);
|
|
||||||
|
|
||||||
if (v9ses->cache)
|
|
||||||
sb->s_d_op = &v9fs_cached_dentry_operations;
|
|
||||||
else
|
|
||||||
sb->s_d_op = &v9fs_dentry_operations;
|
|
||||||
|
|
||||||
inode = v9fs_get_inode(sb, S_IFDIR | mode, 0);
|
|
||||||
if (IS_ERR(inode)) {
|
|
||||||
retval = PTR_ERR(inode);
|
|
||||||
goto release_sb;
|
|
||||||
}
|
|
||||||
|
|
||||||
root = d_make_root(inode);
|
|
||||||
if (!root) {
|
|
||||||
retval = -ENOMEM;
|
|
||||||
goto release_sb;
|
|
||||||
}
|
|
||||||
sb->s_root = root;
|
|
||||||
if (v9fs_proto_dotl(v9ses)) {
|
|
||||||
struct p9_stat_dotl *st = NULL;
|
|
||||||
st = p9_client_getattr_dotl(fid, P9_STATS_BASIC);
|
|
||||||
if (IS_ERR(st)) {
|
|
||||||
retval = PTR_ERR(st);
|
|
||||||
goto release_sb;
|
|
||||||
}
|
|
||||||
root->d_inode->i_ino = v9fs_qid2ino(&st->qid);
|
|
||||||
v9fs_stat2inode_dotl(st, root->d_inode);
|
|
||||||
kfree(st);
|
|
||||||
} else {
|
|
||||||
struct p9_wstat *st = NULL;
|
|
||||||
st = p9_client_stat(fid);
|
|
||||||
if (IS_ERR(st)) {
|
|
||||||
retval = PTR_ERR(st);
|
|
||||||
goto release_sb;
|
|
||||||
}
|
|
||||||
|
|
||||||
root->d_inode->i_ino = v9fs_qid2ino(&st->qid);
|
|
||||||
v9fs_stat2inode(st, root->d_inode, sb);
|
|
||||||
|
|
||||||
p9stat_free(st);
|
|
||||||
kfree(st);
|
|
||||||
}
|
|
||||||
retval = v9fs_get_acl(inode, fid);
|
|
||||||
if (retval)
|
|
||||||
goto release_sb;
|
|
||||||
v9fs_fid_add(root, fid);
|
|
||||||
|
|
||||||
p9_debug(P9_DEBUG_VFS, " simple set mount, return 0\n");
|
|
||||||
return dget(sb->s_root);
|
|
||||||
|
|
||||||
clunk_fid:
|
|
||||||
p9_client_clunk(fid);
|
|
||||||
close_session:
|
|
||||||
v9fs_session_close(v9ses);
|
|
||||||
kfree(v9ses);
|
|
||||||
return ERR_PTR(retval);
|
|
||||||
|
|
||||||
release_sb:
|
|
||||||
/*
|
|
||||||
* we will do the session_close and root dentry release
|
|
||||||
* in the below call. But we need to clunk fid, because we haven't
|
|
||||||
* attached the fid to dentry so it won't get clunked
|
|
||||||
* automatically.
|
|
||||||
*/
|
|
||||||
p9_client_clunk(fid);
|
|
||||||
deactivate_locked_super(sb);
|
|
||||||
return ERR_PTR(retval);
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* v9fs_kill_super - Kill Superblock
|
|
||||||
* @s: superblock
|
|
||||||
*
|
|
||||||
*/
|
|
||||||
|
|
||||||
static void v9fs_kill_super(struct super_block *s)
|
|
||||||
{
|
|
||||||
struct v9fs_session_info *v9ses = s->s_fs_info;
|
|
||||||
|
|
||||||
p9_debug(P9_DEBUG_VFS, " %p\n", s);
|
|
||||||
|
|
||||||
kill_anon_super(s);
|
|
||||||
|
|
||||||
v9fs_session_cancel(v9ses);
|
|
||||||
v9fs_session_close(v9ses);
|
|
||||||
kfree(v9ses);
|
|
||||||
s->s_fs_info = NULL;
|
|
||||||
p9_debug(P9_DEBUG_VFS, "exiting kill_super\n");
|
|
||||||
}
|
|
||||||
|
|
||||||
static void
|
|
||||||
v9fs_umount_begin(struct super_block *sb)
|
|
||||||
{
|
|
||||||
struct v9fs_session_info *v9ses;
|
|
||||||
|
|
||||||
v9ses = sb->s_fs_info;
|
|
||||||
v9fs_session_begin_cancel(v9ses);
|
|
||||||
}
|
|
||||||
|
|
||||||
static int v9fs_statfs(struct dentry *dentry, struct kstatfs *buf)
|
|
||||||
{
|
|
||||||
struct v9fs_session_info *v9ses;
|
|
||||||
struct p9_fid *fid;
|
|
||||||
struct p9_rstatfs rs;
|
|
||||||
int res;
|
|
||||||
|
|
||||||
fid = v9fs_fid_lookup(dentry);
|
|
||||||
if (IS_ERR(fid)) {
|
|
||||||
res = PTR_ERR(fid);
|
|
||||||
goto done;
|
|
||||||
}
|
|
||||||
|
|
||||||
v9ses = v9fs_dentry2v9ses(dentry);
|
|
||||||
if (v9fs_proto_dotl(v9ses)) {
|
|
||||||
res = p9_client_statfs(fid, &rs);
|
|
||||||
if (res == 0) {
|
|
||||||
buf->f_type = rs.type;
|
|
||||||
buf->f_bsize = rs.bsize;
|
|
||||||
buf->f_blocks = rs.blocks;
|
|
||||||
buf->f_bfree = rs.bfree;
|
|
||||||
buf->f_bavail = rs.bavail;
|
|
||||||
buf->f_files = rs.files;
|
|
||||||
buf->f_ffree = rs.ffree;
|
|
||||||
buf->f_fsid.val[0] = rs.fsid & 0xFFFFFFFFUL;
|
|
||||||
buf->f_fsid.val[1] = (rs.fsid >> 32) & 0xFFFFFFFFUL;
|
|
||||||
buf->f_namelen = rs.namelen;
|
|
||||||
}
|
|
||||||
if (res != -ENOSYS)
|
|
||||||
goto done;
|
|
||||||
}
|
|
||||||
res = simple_statfs(dentry, buf);
|
|
||||||
done:
|
|
||||||
return res;
|
|
||||||
}
|
|
||||||
|
|
||||||
static int v9fs_drop_inode(struct inode *inode)
|
|
||||||
{
|
|
||||||
struct v9fs_session_info *v9ses;
|
|
||||||
v9ses = v9fs_inode2v9ses(inode);
|
|
||||||
if (v9ses->cache)
|
|
||||||
return generic_drop_inode(inode);
|
|
||||||
/*
|
|
||||||
* in case of non cached mode always drop the
|
|
||||||
* the inode because we want the inode attribute
|
|
||||||
* to always match that on the server.
|
|
||||||
*/
|
|
||||||
return 1;
|
|
||||||
}
|
|
||||||
|
|
||||||
static int v9fs_write_inode(struct inode *inode,
|
|
||||||
struct writeback_control *wbc)
|
|
||||||
{
|
|
||||||
int ret;
|
|
||||||
struct p9_wstat wstat;
|
|
||||||
struct v9fs_inode *v9inode;
|
|
||||||
/*
|
|
||||||
* send an fsync request to server irrespective of
|
|
||||||
* wbc->sync_mode.
|
|
||||||
*/
|
|
||||||
p9_debug(P9_DEBUG_VFS, "%s: inode %p\n", __func__, inode);
|
|
||||||
v9inode = V9FS_I(inode);
|
|
||||||
if (!v9inode->writeback_fid)
|
|
||||||
return 0;
|
|
||||||
v9fs_blank_wstat(&wstat);
|
|
||||||
|
|
||||||
ret = p9_client_wstat(v9inode->writeback_fid, &wstat);
|
|
||||||
if (ret < 0) {
|
|
||||||
__mark_inode_dirty(inode, I_DIRTY_DATASYNC);
|
|
||||||
return ret;
|
|
||||||
}
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
static int v9fs_write_inode_dotl(struct inode *inode,
|
|
||||||
struct writeback_control *wbc)
|
|
||||||
{
|
|
||||||
int ret;
|
|
||||||
struct v9fs_inode *v9inode;
|
|
||||||
/*
|
|
||||||
* send an fsync request to server irrespective of
|
|
||||||
* wbc->sync_mode.
|
|
||||||
*/
|
|
||||||
p9_debug(P9_DEBUG_VFS, "%s: inode %p\n", __func__, inode);
|
|
||||||
v9inode = V9FS_I(inode);
|
|
||||||
if (!v9inode->writeback_fid)
|
|
||||||
return 0;
|
|
||||||
ret = p9_client_fsync(v9inode->writeback_fid, 0);
|
|
||||||
if (ret < 0) {
|
|
||||||
__mark_inode_dirty(inode, I_DIRTY_DATASYNC);
|
|
||||||
return ret;
|
|
||||||
}
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
static const struct super_operations v9fs_super_ops = {
|
|
||||||
.alloc_inode = v9fs_alloc_inode,
|
|
||||||
.destroy_inode = v9fs_destroy_inode,
|
|
||||||
.statfs = simple_statfs,
|
|
||||||
.evict_inode = v9fs_evict_inode,
|
|
||||||
.show_options = generic_show_options,
|
|
||||||
.umount_begin = v9fs_umount_begin,
|
|
||||||
.write_inode = v9fs_write_inode,
|
|
||||||
};
|
|
||||||
|
|
||||||
static const struct super_operations v9fs_super_ops_dotl = {
|
|
||||||
.alloc_inode = v9fs_alloc_inode,
|
|
||||||
.destroy_inode = v9fs_destroy_inode,
|
|
||||||
.statfs = v9fs_statfs,
|
|
||||||
.drop_inode = v9fs_drop_inode,
|
|
||||||
.evict_inode = v9fs_evict_inode,
|
|
||||||
.show_options = generic_show_options,
|
|
||||||
.umount_begin = v9fs_umount_begin,
|
|
||||||
.write_inode = v9fs_write_inode_dotl,
|
|
||||||
};
|
|
||||||
|
|
||||||
struct file_system_type v9fs_fs_type = {
|
|
||||||
.name = "9p",
|
|
||||||
.mount = v9fs_mount,
|
|
||||||
.kill_sb = v9fs_kill_super,
|
|
||||||
.owner = THIS_MODULE,
|
|
||||||
.fs_flags = FS_RENAME_DOES_D_MOVE,
|
|
||||||
};
|
|
||||||
MODULE_ALIAS_FS("9p");
|
|
@ -1,175 +0,0 @@
|
|||||||
/*
|
|
||||||
* Copyright IBM Corporation, 2010
|
|
||||||
* Author Aneesh Kumar K.V <aneesh.kumar@linux.vnet.ibm.com>
|
|
||||||
*
|
|
||||||
* This program is free software; you can redistribute it and/or modify it
|
|
||||||
* under the terms of version 2.1 of the GNU Lesser General Public License
|
|
||||||
* as published by the Free Software Foundation.
|
|
||||||
*
|
|
||||||
* This program is distributed in the hope that it would be useful, but
|
|
||||||
* WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
||||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
|
|
||||||
*
|
|
||||||
*/
|
|
||||||
|
|
||||||
#include <linux/module.h>
|
|
||||||
#include <linux/fs.h>
|
|
||||||
#include <linux/sched.h>
|
|
||||||
#include <net/9p/9p.h>
|
|
||||||
#include <net/9p/client.h>
|
|
||||||
|
|
||||||
#include "fid.h"
|
|
||||||
#include "xattr.h"
|
|
||||||
|
|
||||||
ssize_t v9fs_fid_xattr_get(struct p9_fid *fid, const char *name,
|
|
||||||
void *buffer, size_t buffer_size)
|
|
||||||
{
|
|
||||||
ssize_t retval;
|
|
||||||
int msize, read_count;
|
|
||||||
u64 offset = 0, attr_size;
|
|
||||||
struct p9_fid *attr_fid;
|
|
||||||
|
|
||||||
attr_fid = p9_client_xattrwalk(fid, name, &attr_size);
|
|
||||||
if (IS_ERR(attr_fid)) {
|
|
||||||
retval = PTR_ERR(attr_fid);
|
|
||||||
p9_debug(P9_DEBUG_VFS, "p9_client_attrwalk failed %zd\n",
|
|
||||||
retval);
|
|
||||||
attr_fid = NULL;
|
|
||||||
goto error;
|
|
||||||
}
|
|
||||||
if (!buffer_size) {
|
|
||||||
/* request to get the attr_size */
|
|
||||||
retval = attr_size;
|
|
||||||
goto error;
|
|
||||||
}
|
|
||||||
if (attr_size > buffer_size) {
|
|
||||||
retval = -ERANGE;
|
|
||||||
goto error;
|
|
||||||
}
|
|
||||||
msize = attr_fid->clnt->msize;
|
|
||||||
while (attr_size) {
|
|
||||||
if (attr_size > (msize - P9_IOHDRSZ))
|
|
||||||
read_count = msize - P9_IOHDRSZ;
|
|
||||||
else
|
|
||||||
read_count = attr_size;
|
|
||||||
read_count = p9_client_read(attr_fid, ((char *)buffer)+offset,
|
|
||||||
NULL, offset, read_count);
|
|
||||||
if (read_count < 0) {
|
|
||||||
/* error in xattr read */
|
|
||||||
retval = read_count;
|
|
||||||
goto error;
|
|
||||||
}
|
|
||||||
offset += read_count;
|
|
||||||
attr_size -= read_count;
|
|
||||||
}
|
|
||||||
/* Total read xattr bytes */
|
|
||||||
retval = offset;
|
|
||||||
error:
|
|
||||||
if (attr_fid)
|
|
||||||
p9_client_clunk(attr_fid);
|
|
||||||
return retval;
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
/*
|
|
||||||
* v9fs_xattr_get()
|
|
||||||
*
|
|
||||||
* Copy an extended attribute into the buffer
|
|
||||||
* provided, or compute the buffer size required.
|
|
||||||
* Buffer is NULL to compute the size of the buffer required.
|
|
||||||
*
|
|
||||||
* Returns a negative error number on failure, or the number of bytes
|
|
||||||
* used / required on success.
|
|
||||||
*/
|
|
||||||
ssize_t v9fs_xattr_get(struct dentry *dentry, const char *name,
|
|
||||||
void *buffer, size_t buffer_size)
|
|
||||||
{
|
|
||||||
struct p9_fid *fid;
|
|
||||||
|
|
||||||
p9_debug(P9_DEBUG_VFS, "name = %s value_len = %zu\n",
|
|
||||||
name, buffer_size);
|
|
||||||
fid = v9fs_fid_lookup(dentry);
|
|
||||||
if (IS_ERR(fid))
|
|
||||||
return PTR_ERR(fid);
|
|
||||||
|
|
||||||
return v9fs_fid_xattr_get(fid, name, buffer, buffer_size);
|
|
||||||
}
|
|
||||||
|
|
||||||
/*
|
|
||||||
* v9fs_xattr_set()
|
|
||||||
*
|
|
||||||
* Create, replace or remove an extended attribute for this inode. Buffer
|
|
||||||
* is NULL to remove an existing extended attribute, and non-NULL to
|
|
||||||
* either replace an existing extended attribute, or create a new extended
|
|
||||||
* attribute. The flags XATTR_REPLACE and XATTR_CREATE
|
|
||||||
* specify that an extended attribute must exist and must not exist
|
|
||||||
* previous to the call, respectively.
|
|
||||||
*
|
|
||||||
* Returns 0, or a negative error number on failure.
|
|
||||||
*/
|
|
||||||
int v9fs_xattr_set(struct dentry *dentry, const char *name,
|
|
||||||
const void *value, size_t value_len, int flags)
|
|
||||||
{
|
|
||||||
struct p9_fid *fid = v9fs_fid_lookup(dentry);
|
|
||||||
if (IS_ERR(fid))
|
|
||||||
return PTR_ERR(fid);
|
|
||||||
return v9fs_fid_xattr_set(fid, name, value, value_len, flags);
|
|
||||||
}
|
|
||||||
|
|
||||||
int v9fs_fid_xattr_set(struct p9_fid *fid, const char *name,
|
|
||||||
const void *value, size_t value_len, int flags)
|
|
||||||
{
|
|
||||||
u64 offset = 0;
|
|
||||||
int retval, msize, write_count;
|
|
||||||
|
|
||||||
p9_debug(P9_DEBUG_VFS, "name = %s value_len = %zu flags = %d\n",
|
|
||||||
name, value_len, flags);
|
|
||||||
|
|
||||||
/* Clone it */
|
|
||||||
fid = p9_client_walk(fid, 0, NULL, 1);
|
|
||||||
if (IS_ERR(fid))
|
|
||||||
return PTR_ERR(fid);
|
|
||||||
|
|
||||||
/*
|
|
||||||
* On success fid points to xattr
|
|
||||||
*/
|
|
||||||
retval = p9_client_xattrcreate(fid, name, value_len, flags);
|
|
||||||
if (retval < 0) {
|
|
||||||
p9_debug(P9_DEBUG_VFS, "p9_client_xattrcreate failed %d\n",
|
|
||||||
retval);
|
|
||||||
p9_client_clunk(fid);
|
|
||||||
return retval;
|
|
||||||
}
|
|
||||||
msize = fid->clnt->msize;
|
|
||||||
while (value_len) {
|
|
||||||
if (value_len > (msize - P9_IOHDRSZ))
|
|
||||||
write_count = msize - P9_IOHDRSZ;
|
|
||||||
else
|
|
||||||
write_count = value_len;
|
|
||||||
write_count = p9_client_write(fid, ((char *)value)+offset,
|
|
||||||
NULL, offset, write_count);
|
|
||||||
if (write_count < 0) {
|
|
||||||
/* error in xattr write */
|
|
||||||
retval = write_count;
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
offset += write_count;
|
|
||||||
value_len -= write_count;
|
|
||||||
}
|
|
||||||
return p9_client_clunk(fid);
|
|
||||||
}
|
|
||||||
|
|
||||||
ssize_t v9fs_listxattr(struct dentry *dentry, char *buffer, size_t buffer_size)
|
|
||||||
{
|
|
||||||
return v9fs_xattr_get(dentry, NULL, buffer, buffer_size);
|
|
||||||
}
|
|
||||||
|
|
||||||
const struct xattr_handler *v9fs_xattr_handlers[] = {
|
|
||||||
&v9fs_xattr_user_handler,
|
|
||||||
#ifdef CONFIG_9P_FS_POSIX_ACL
|
|
||||||
&v9fs_xattr_acl_access_handler,
|
|
||||||
&v9fs_xattr_acl_default_handler,
|
|
||||||
#endif
|
|
||||||
NULL
|
|
||||||
};
|
|
@ -1,35 +0,0 @@
|
|||||||
/*
|
|
||||||
* Copyright IBM Corporation, 2010
|
|
||||||
* Author Aneesh Kumar K.V <aneesh.kumar@linux.vnet.ibm.com>
|
|
||||||
*
|
|
||||||
* This program is free software; you can redistribute it and/or modify it
|
|
||||||
* under the terms of version 2.1 of the GNU Lesser General Public License
|
|
||||||
* as published by the Free Software Foundation.
|
|
||||||
*
|
|
||||||
* This program is distributed in the hope that it would be useful, but
|
|
||||||
* WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
||||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
|
|
||||||
*
|
|
||||||
*/
|
|
||||||
#ifndef FS_9P_XATTR_H
|
|
||||||
#define FS_9P_XATTR_H
|
|
||||||
|
|
||||||
#include <linux/xattr.h>
|
|
||||||
#include <net/9p/9p.h>
|
|
||||||
#include <net/9p/client.h>
|
|
||||||
|
|
||||||
extern const struct xattr_handler *v9fs_xattr_handlers[];
|
|
||||||
extern struct xattr_handler v9fs_xattr_user_handler;
|
|
||||||
extern const struct xattr_handler v9fs_xattr_acl_access_handler;
|
|
||||||
extern const struct xattr_handler v9fs_xattr_acl_default_handler;
|
|
||||||
|
|
||||||
extern ssize_t v9fs_fid_xattr_get(struct p9_fid *, const char *,
|
|
||||||
void *, size_t);
|
|
||||||
extern ssize_t v9fs_xattr_get(struct dentry *, const char *,
|
|
||||||
void *, size_t);
|
|
||||||
extern int v9fs_fid_xattr_set(struct p9_fid *, const char *,
|
|
||||||
const void *, size_t, int);
|
|
||||||
extern int v9fs_xattr_set(struct dentry *, const char *,
|
|
||||||
const void *, size_t, int);
|
|
||||||
extern ssize_t v9fs_listxattr(struct dentry *, char *, size_t);
|
|
||||||
#endif /* FS_9P_XATTR_H */
|
|
@ -1,80 +0,0 @@
|
|||||||
/*
|
|
||||||
* Copyright IBM Corporation, 2010
|
|
||||||
* Author Aneesh Kumar K.V <aneesh.kumar@linux.vnet.ibm.com>
|
|
||||||
*
|
|
||||||
* This program is free software; you can redistribute it and/or modify it
|
|
||||||
* under the terms of version 2.1 of the GNU Lesser General Public License
|
|
||||||
* as published by the Free Software Foundation.
|
|
||||||
*
|
|
||||||
* This program is distributed in the hope that it would be useful, but
|
|
||||||
* WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
||||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
|
|
||||||
*
|
|
||||||
*/
|
|
||||||
|
|
||||||
|
|
||||||
#include <linux/module.h>
|
|
||||||
#include <linux/string.h>
|
|
||||||
#include <linux/fs.h>
|
|
||||||
#include <linux/slab.h>
|
|
||||||
#include "xattr.h"
|
|
||||||
|
|
||||||
static int v9fs_xattr_user_get(struct dentry *dentry, const char *name,
|
|
||||||
void *buffer, size_t size, int type)
|
|
||||||
{
|
|
||||||
int retval;
|
|
||||||
char *full_name;
|
|
||||||
size_t name_len;
|
|
||||||
size_t prefix_len = XATTR_USER_PREFIX_LEN;
|
|
||||||
|
|
||||||
if (name == NULL)
|
|
||||||
return -EINVAL;
|
|
||||||
|
|
||||||
if (strcmp(name, "") == 0)
|
|
||||||
return -EINVAL;
|
|
||||||
|
|
||||||
name_len = strlen(name);
|
|
||||||
full_name = kmalloc(prefix_len + name_len + 1 , GFP_KERNEL);
|
|
||||||
if (!full_name)
|
|
||||||
return -ENOMEM;
|
|
||||||
memcpy(full_name, XATTR_USER_PREFIX, prefix_len);
|
|
||||||
memcpy(full_name+prefix_len, name, name_len);
|
|
||||||
full_name[prefix_len + name_len] = '\0';
|
|
||||||
|
|
||||||
retval = v9fs_xattr_get(dentry, full_name, buffer, size);
|
|
||||||
kfree(full_name);
|
|
||||||
return retval;
|
|
||||||
}
|
|
||||||
|
|
||||||
static int v9fs_xattr_user_set(struct dentry *dentry, const char *name,
|
|
||||||
const void *value, size_t size, int flags, int type)
|
|
||||||
{
|
|
||||||
int retval;
|
|
||||||
char *full_name;
|
|
||||||
size_t name_len;
|
|
||||||
size_t prefix_len = XATTR_USER_PREFIX_LEN;
|
|
||||||
|
|
||||||
if (name == NULL)
|
|
||||||
return -EINVAL;
|
|
||||||
|
|
||||||
if (strcmp(name, "") == 0)
|
|
||||||
return -EINVAL;
|
|
||||||
|
|
||||||
name_len = strlen(name);
|
|
||||||
full_name = kmalloc(prefix_len + name_len + 1 , GFP_KERNEL);
|
|
||||||
if (!full_name)
|
|
||||||
return -ENOMEM;
|
|
||||||
memcpy(full_name, XATTR_USER_PREFIX, prefix_len);
|
|
||||||
memcpy(full_name + prefix_len, name, name_len);
|
|
||||||
full_name[prefix_len + name_len] = '\0';
|
|
||||||
|
|
||||||
retval = v9fs_xattr_set(dentry, full_name, value, size, flags);
|
|
||||||
kfree(full_name);
|
|
||||||
return retval;
|
|
||||||
}
|
|
||||||
|
|
||||||
struct xattr_handler v9fs_xattr_user_handler = {
|
|
||||||
.prefix = XATTR_USER_PREFIX,
|
|
||||||
.get = v9fs_xattr_user_get,
|
|
||||||
.set = v9fs_xattr_user_set,
|
|
||||||
};
|
|
@ -1,35 +0,0 @@
|
|||||||
obj-m := 9p.o
|
|
||||||
|
|
||||||
9p-objs := \
|
|
||||||
vfs_super.o \
|
|
||||||
vfs_inode.o \
|
|
||||||
vfs_inode_dotl.o \
|
|
||||||
vfs_addr.o \
|
|
||||||
vfs_file.o \
|
|
||||||
vfs_dir.o \
|
|
||||||
vfs_dentry.o \
|
|
||||||
v9fs.o \
|
|
||||||
fid.o \
|
|
||||||
xattr.o
|
|
||||||
|
|
||||||
9p-y += cache.o
|
|
||||||
9p-n += acl.o
|
|
||||||
|
|
||||||
obj-m := 9pnet.o
|
|
||||||
obj-m += 9pnet_virtio.o
|
|
||||||
obj-n += 9pnet_rdma.o
|
|
||||||
|
|
||||||
9pnet-objs := \
|
|
||||||
mod.o \
|
|
||||||
client.o \
|
|
||||||
error.o \
|
|
||||||
util.o \
|
|
||||||
protocol.o \
|
|
||||||
trans_fd.o \
|
|
||||||
trans_common.o \
|
|
||||||
|
|
||||||
9pnet_virtio-objs := \
|
|
||||||
trans_virtio.o
|
|
||||||
|
|
||||||
9pnet_rdma-objs := \
|
|
||||||
trans_rdma.o
|
|
@ -1,338 +0,0 @@
|
|||||||
/*
|
|
||||||
* Copyright IBM Corporation, 2010
|
|
||||||
* Author Aneesh Kumar K.V <aneesh.kumar@linux.vnet.ibm.com>
|
|
||||||
*
|
|
||||||
* This program is free software; you can redistribute it and/or modify it
|
|
||||||
* under the terms of version 2.1 of the GNU Lesser General Public License
|
|
||||||
* as published by the Free Software Foundation.
|
|
||||||
*
|
|
||||||
* This program is distributed in the hope that it would be useful, but
|
|
||||||
* WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
||||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
|
|
||||||
*
|
|
||||||
*/
|
|
||||||
|
|
||||||
#include <linux/module.h>
|
|
||||||
#include <linux/fs.h>
|
|
||||||
#include <net/9p/9p.h>
|
|
||||||
#include <net/9p/client.h>
|
|
||||||
#include <linux/slab.h>
|
|
||||||
#include <linux/sched.h>
|
|
||||||
#include <linux/posix_acl_xattr.h>
|
|
||||||
#include "xattr.h"
|
|
||||||
#include "acl.h"
|
|
||||||
#include "v9fs.h"
|
|
||||||
#include "v9fs_vfs.h"
|
|
||||||
#include "fid.h"
|
|
||||||
|
|
||||||
static struct posix_acl *__v9fs_get_acl(struct p9_fid *fid, char *name)
|
|
||||||
{
|
|
||||||
ssize_t size;
|
|
||||||
void *value = NULL;
|
|
||||||
struct posix_acl *acl = NULL;
|
|
||||||
|
|
||||||
size = v9fs_fid_xattr_get(fid, name, NULL, 0);
|
|
||||||
if (size > 0) {
|
|
||||||
value = kzalloc(size, GFP_NOFS);
|
|
||||||
if (!value)
|
|
||||||
return ERR_PTR(-ENOMEM);
|
|
||||||
size = v9fs_fid_xattr_get(fid, name, value, size);
|
|
||||||
if (size > 0) {
|
|
||||||
acl = posix_acl_from_xattr(&init_user_ns, value, size);
|
|
||||||
if (IS_ERR(acl))
|
|
||||||
goto err_out;
|
|
||||||
}
|
|
||||||
} else if (size == -ENODATA || size == 0 ||
|
|
||||||
size == -ENOSYS || size == -EOPNOTSUPP) {
|
|
||||||
acl = NULL;
|
|
||||||
} else
|
|
||||||
acl = ERR_PTR(-EIO);
|
|
||||||
|
|
||||||
err_out:
|
|
||||||
kfree(value);
|
|
||||||
return acl;
|
|
||||||
}
|
|
||||||
|
|
||||||
int v9fs_get_acl(struct inode *inode, struct p9_fid *fid)
|
|
||||||
{
|
|
||||||
int retval = 0;
|
|
||||||
struct posix_acl *pacl, *dacl;
|
|
||||||
struct v9fs_session_info *v9ses;
|
|
||||||
|
|
||||||
v9ses = v9fs_inode2v9ses(inode);
|
|
||||||
if (((v9ses->flags & V9FS_ACCESS_MASK) != V9FS_ACCESS_CLIENT) ||
|
|
||||||
((v9ses->flags & V9FS_ACL_MASK) != V9FS_POSIX_ACL)) {
|
|
||||||
set_cached_acl(inode, ACL_TYPE_DEFAULT, NULL);
|
|
||||||
set_cached_acl(inode, ACL_TYPE_ACCESS, NULL);
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
/* get the default/access acl values and cache them */
|
|
||||||
dacl = __v9fs_get_acl(fid, POSIX_ACL_XATTR_DEFAULT);
|
|
||||||
pacl = __v9fs_get_acl(fid, POSIX_ACL_XATTR_ACCESS);
|
|
||||||
|
|
||||||
if (!IS_ERR(dacl) && !IS_ERR(pacl)) {
|
|
||||||
set_cached_acl(inode, ACL_TYPE_DEFAULT, dacl);
|
|
||||||
set_cached_acl(inode, ACL_TYPE_ACCESS, pacl);
|
|
||||||
} else
|
|
||||||
retval = -EIO;
|
|
||||||
|
|
||||||
if (!IS_ERR(dacl))
|
|
||||||
posix_acl_release(dacl);
|
|
||||||
|
|
||||||
if (!IS_ERR(pacl))
|
|
||||||
posix_acl_release(pacl);
|
|
||||||
|
|
||||||
return retval;
|
|
||||||
}
|
|
||||||
|
|
||||||
static struct posix_acl *v9fs_get_cached_acl(struct inode *inode, int type)
|
|
||||||
{
|
|
||||||
struct posix_acl *acl;
|
|
||||||
/*
|
|
||||||
* 9p Always cache the acl value when
|
|
||||||
* instantiating the inode (v9fs_inode_from_fid)
|
|
||||||
*/
|
|
||||||
acl = get_cached_acl(inode, type);
|
|
||||||
BUG_ON(acl == ACL_NOT_CACHED);
|
|
||||||
return acl;
|
|
||||||
}
|
|
||||||
|
|
||||||
struct posix_acl *v9fs_iop_get_acl(struct inode *inode, int type)
|
|
||||||
{
|
|
||||||
struct v9fs_session_info *v9ses;
|
|
||||||
|
|
||||||
v9ses = v9fs_inode2v9ses(inode);
|
|
||||||
if (((v9ses->flags & V9FS_ACCESS_MASK) != V9FS_ACCESS_CLIENT) ||
|
|
||||||
((v9ses->flags & V9FS_ACL_MASK) != V9FS_POSIX_ACL)) {
|
|
||||||
/*
|
|
||||||
* On access = client and acl = on mode get the acl
|
|
||||||
* values from the server
|
|
||||||
*/
|
|
||||||
return NULL;
|
|
||||||
}
|
|
||||||
return v9fs_get_cached_acl(inode, type);
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
static int v9fs_set_acl(struct p9_fid *fid, int type, struct posix_acl *acl)
|
|
||||||
{
|
|
||||||
int retval;
|
|
||||||
char *name;
|
|
||||||
size_t size;
|
|
||||||
void *buffer;
|
|
||||||
if (!acl)
|
|
||||||
return 0;
|
|
||||||
|
|
||||||
/* Set a setxattr request to server */
|
|
||||||
size = posix_acl_xattr_size(acl->a_count);
|
|
||||||
buffer = kmalloc(size, GFP_KERNEL);
|
|
||||||
if (!buffer)
|
|
||||||
return -ENOMEM;
|
|
||||||
retval = posix_acl_to_xattr(&init_user_ns, acl, buffer, size);
|
|
||||||
if (retval < 0)
|
|
||||||
goto err_free_out;
|
|
||||||
switch (type) {
|
|
||||||
case ACL_TYPE_ACCESS:
|
|
||||||
name = POSIX_ACL_XATTR_ACCESS;
|
|
||||||
break;
|
|
||||||
case ACL_TYPE_DEFAULT:
|
|
||||||
name = POSIX_ACL_XATTR_DEFAULT;
|
|
||||||
break;
|
|
||||||
default:
|
|
||||||
BUG();
|
|
||||||
}
|
|
||||||
retval = v9fs_fid_xattr_set(fid, name, buffer, size, 0);
|
|
||||||
err_free_out:
|
|
||||||
kfree(buffer);
|
|
||||||
return retval;
|
|
||||||
}
|
|
||||||
|
|
||||||
int v9fs_acl_chmod(struct inode *inode, struct p9_fid *fid)
|
|
||||||
{
|
|
||||||
int retval = 0;
|
|
||||||
struct posix_acl *acl;
|
|
||||||
|
|
||||||
if (S_ISLNK(inode->i_mode))
|
|
||||||
return -EOPNOTSUPP;
|
|
||||||
acl = v9fs_get_cached_acl(inode, ACL_TYPE_ACCESS);
|
|
||||||
if (acl) {
|
|
||||||
retval = __posix_acl_chmod(&acl, GFP_KERNEL, inode->i_mode);
|
|
||||||
if (retval)
|
|
||||||
return retval;
|
|
||||||
set_cached_acl(inode, ACL_TYPE_ACCESS, acl);
|
|
||||||
retval = v9fs_set_acl(fid, ACL_TYPE_ACCESS, acl);
|
|
||||||
posix_acl_release(acl);
|
|
||||||
}
|
|
||||||
return retval;
|
|
||||||
}
|
|
||||||
|
|
||||||
int v9fs_set_create_acl(struct inode *inode, struct p9_fid *fid,
|
|
||||||
struct posix_acl *dacl, struct posix_acl *acl)
|
|
||||||
{
|
|
||||||
set_cached_acl(inode, ACL_TYPE_DEFAULT, dacl);
|
|
||||||
set_cached_acl(inode, ACL_TYPE_ACCESS, acl);
|
|
||||||
v9fs_set_acl(fid, ACL_TYPE_DEFAULT, dacl);
|
|
||||||
v9fs_set_acl(fid, ACL_TYPE_ACCESS, acl);
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
void v9fs_put_acl(struct posix_acl *dacl,
|
|
||||||
struct posix_acl *acl)
|
|
||||||
{
|
|
||||||
posix_acl_release(dacl);
|
|
||||||
posix_acl_release(acl);
|
|
||||||
}
|
|
||||||
|
|
||||||
int v9fs_acl_mode(struct inode *dir, umode_t *modep,
|
|
||||||
struct posix_acl **dpacl, struct posix_acl **pacl)
|
|
||||||
{
|
|
||||||
int retval = 0;
|
|
||||||
umode_t mode = *modep;
|
|
||||||
struct posix_acl *acl = NULL;
|
|
||||||
|
|
||||||
if (!S_ISLNK(mode)) {
|
|
||||||
acl = v9fs_get_cached_acl(dir, ACL_TYPE_DEFAULT);
|
|
||||||
if (IS_ERR(acl))
|
|
||||||
return PTR_ERR(acl);
|
|
||||||
if (!acl)
|
|
||||||
mode &= ~current_umask();
|
|
||||||
}
|
|
||||||
if (acl) {
|
|
||||||
if (S_ISDIR(mode))
|
|
||||||
*dpacl = posix_acl_dup(acl);
|
|
||||||
retval = __posix_acl_create(&acl, GFP_NOFS, &mode);
|
|
||||||
if (retval < 0)
|
|
||||||
return retval;
|
|
||||||
if (retval > 0)
|
|
||||||
*pacl = acl;
|
|
||||||
else
|
|
||||||
posix_acl_release(acl);
|
|
||||||
}
|
|
||||||
*modep = mode;
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
static int v9fs_xattr_get_acl(const struct xattr_handler *handler,
|
|
||||||
struct dentry *dentry, const char *name,
|
|
||||||
void *buffer, size_t size)
|
|
||||||
{
|
|
||||||
struct v9fs_session_info *v9ses;
|
|
||||||
struct posix_acl *acl;
|
|
||||||
int error;
|
|
||||||
|
|
||||||
if (strcmp(name, "") != 0)
|
|
||||||
return -EINVAL;
|
|
||||||
|
|
||||||
v9ses = v9fs_dentry2v9ses(dentry);
|
|
||||||
/*
|
|
||||||
* We allow set/get/list of acl when access=client is not specified
|
|
||||||
*/
|
|
||||||
if ((v9ses->flags & V9FS_ACCESS_MASK) != V9FS_ACCESS_CLIENT)
|
|
||||||
return v9fs_xattr_get(dentry, handler->prefix, buffer, size);
|
|
||||||
|
|
||||||
acl = v9fs_get_cached_acl(d_inode(dentry), handler->flags);
|
|
||||||
if (IS_ERR(acl))
|
|
||||||
return PTR_ERR(acl);
|
|
||||||
if (acl == NULL)
|
|
||||||
return -ENODATA;
|
|
||||||
error = posix_acl_to_xattr(&init_user_ns, acl, buffer, size);
|
|
||||||
posix_acl_release(acl);
|
|
||||||
|
|
||||||
return error;
|
|
||||||
}
|
|
||||||
|
|
||||||
static int v9fs_xattr_set_acl(const struct xattr_handler *handler,
|
|
||||||
struct dentry *dentry, const char *name,
|
|
||||||
const void *value, size_t size, int flags)
|
|
||||||
{
|
|
||||||
int retval;
|
|
||||||
struct posix_acl *acl;
|
|
||||||
struct v9fs_session_info *v9ses;
|
|
||||||
struct inode *inode = d_inode(dentry);
|
|
||||||
|
|
||||||
if (strcmp(name, "") != 0)
|
|
||||||
return -EINVAL;
|
|
||||||
|
|
||||||
v9ses = v9fs_dentry2v9ses(dentry);
|
|
||||||
/*
|
|
||||||
* set the attribute on the remote. Without even looking at the
|
|
||||||
* xattr value. We leave it to the server to validate
|
|
||||||
*/
|
|
||||||
if ((v9ses->flags & V9FS_ACCESS_MASK) != V9FS_ACCESS_CLIENT)
|
|
||||||
return v9fs_xattr_set(dentry, handler->prefix, value, size,
|
|
||||||
flags);
|
|
||||||
|
|
||||||
if (S_ISLNK(inode->i_mode))
|
|
||||||
return -EOPNOTSUPP;
|
|
||||||
if (!inode_owner_or_capable(inode))
|
|
||||||
return -EPERM;
|
|
||||||
if (value) {
|
|
||||||
/* update the cached acl value */
|
|
||||||
acl = posix_acl_from_xattr(&init_user_ns, value, size);
|
|
||||||
if (IS_ERR(acl))
|
|
||||||
return PTR_ERR(acl);
|
|
||||||
else if (acl) {
|
|
||||||
retval = posix_acl_valid(acl);
|
|
||||||
if (retval)
|
|
||||||
goto err_out;
|
|
||||||
}
|
|
||||||
} else
|
|
||||||
acl = NULL;
|
|
||||||
|
|
||||||
switch (handler->flags) {
|
|
||||||
case ACL_TYPE_ACCESS:
|
|
||||||
if (acl) {
|
|
||||||
struct iattr iattr;
|
|
||||||
struct posix_acl *old_acl = acl;
|
|
||||||
|
|
||||||
retval = posix_acl_update_mode(inode, &iattr.ia_mode, &acl);
|
|
||||||
if (retval)
|
|
||||||
goto err_out;
|
|
||||||
if (!acl) {
|
|
||||||
/*
|
|
||||||
* ACL can be represented
|
|
||||||
* by the mode bits. So don't
|
|
||||||
* update ACL.
|
|
||||||
*/
|
|
||||||
posix_acl_release(old_acl);
|
|
||||||
value = NULL;
|
|
||||||
size = 0;
|
|
||||||
}
|
|
||||||
iattr.ia_valid = ATTR_MODE;
|
|
||||||
/* FIXME should we update ctime ?
|
|
||||||
* What is the following setxattr update the
|
|
||||||
* mode ?
|
|
||||||
*/
|
|
||||||
v9fs_vfs_setattr_dotl(dentry, &iattr);
|
|
||||||
}
|
|
||||||
break;
|
|
||||||
case ACL_TYPE_DEFAULT:
|
|
||||||
if (!S_ISDIR(inode->i_mode)) {
|
|
||||||
retval = acl ? -EINVAL : 0;
|
|
||||||
goto err_out;
|
|
||||||
}
|
|
||||||
break;
|
|
||||||
default:
|
|
||||||
BUG();
|
|
||||||
}
|
|
||||||
retval = v9fs_xattr_set(dentry, handler->prefix, value, size, flags);
|
|
||||||
if (!retval)
|
|
||||||
set_cached_acl(inode, handler->flags, acl);
|
|
||||||
err_out:
|
|
||||||
posix_acl_release(acl);
|
|
||||||
return retval;
|
|
||||||
}
|
|
||||||
|
|
||||||
const struct xattr_handler v9fs_xattr_acl_access_handler = {
|
|
||||||
.prefix = POSIX_ACL_XATTR_ACCESS,
|
|
||||||
.flags = ACL_TYPE_ACCESS,
|
|
||||||
.get = v9fs_xattr_get_acl,
|
|
||||||
.set = v9fs_xattr_set_acl,
|
|
||||||
};
|
|
||||||
|
|
||||||
const struct xattr_handler v9fs_xattr_acl_default_handler = {
|
|
||||||
.prefix = POSIX_ACL_XATTR_DEFAULT,
|
|
||||||
.flags = ACL_TYPE_DEFAULT,
|
|
||||||
.get = v9fs_xattr_get_acl,
|
|
||||||
.set = v9fs_xattr_set_acl,
|
|
||||||
};
|
|
@ -1,55 +0,0 @@
|
|||||||
/*
|
|
||||||
* Copyright IBM Corporation, 2010
|
|
||||||
* Author Aneesh Kumar K.V <aneesh.kumar@linux.vnet.ibm.com>
|
|
||||||
*
|
|
||||||
* This program is free software; you can redistribute it and/or modify it
|
|
||||||
* under the terms of version 2.1 of the GNU Lesser General Public License
|
|
||||||
* as published by the Free Software Foundation.
|
|
||||||
*
|
|
||||||
* This program is distributed in the hope that it would be useful, but
|
|
||||||
* WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
||||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
|
|
||||||
*
|
|
||||||
*/
|
|
||||||
#ifndef FS_9P_ACL_H
|
|
||||||
#define FS_9P_ACL_H
|
|
||||||
|
|
||||||
#ifdef CONFIG_9P_FS_POSIX_ACL
|
|
||||||
extern int v9fs_get_acl(struct inode *, struct p9_fid *);
|
|
||||||
extern struct posix_acl *v9fs_iop_get_acl(struct inode *inode, int type);
|
|
||||||
extern int v9fs_acl_chmod(struct inode *, struct p9_fid *);
|
|
||||||
extern int v9fs_set_create_acl(struct inode *, struct p9_fid *,
|
|
||||||
struct posix_acl *, struct posix_acl *);
|
|
||||||
extern int v9fs_acl_mode(struct inode *dir, umode_t *modep,
|
|
||||||
struct posix_acl **dpacl, struct posix_acl **pacl);
|
|
||||||
extern void v9fs_put_acl(struct posix_acl *dacl, struct posix_acl *acl);
|
|
||||||
#else
|
|
||||||
#define v9fs_iop_get_acl NULL
|
|
||||||
static inline int v9fs_get_acl(struct inode *inode, struct p9_fid *fid)
|
|
||||||
{
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
static inline int v9fs_acl_chmod(struct inode *inode, struct p9_fid *fid)
|
|
||||||
{
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
static inline int v9fs_set_create_acl(struct inode *inode,
|
|
||||||
struct p9_fid *fid,
|
|
||||||
struct posix_acl *dacl,
|
|
||||||
struct posix_acl *acl)
|
|
||||||
{
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
static inline void v9fs_put_acl(struct posix_acl *dacl,
|
|
||||||
struct posix_acl *acl)
|
|
||||||
{
|
|
||||||
}
|
|
||||||
static inline int v9fs_acl_mode(struct inode *dir, umode_t *modep,
|
|
||||||
struct posix_acl **dpacl,
|
|
||||||
struct posix_acl **pacl)
|
|
||||||
{
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
#endif
|
|
||||||
#endif /* FS_9P_XATTR_H */
|
|
@ -1,414 +0,0 @@
|
|||||||
/*
|
|
||||||
* V9FS cache definitions.
|
|
||||||
*
|
|
||||||
* Copyright (C) 2009 by Abhishek Kulkarni <adkulkar@umail.iu.edu>
|
|
||||||
*
|
|
||||||
* This program is free software; you can redistribute it and/or modify
|
|
||||||
* it under the terms of the GNU General Public License version 2
|
|
||||||
* as published by the Free Software Foundation.
|
|
||||||
*
|
|
||||||
* This program is distributed in the hope that it will be useful,
|
|
||||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
||||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
||||||
* GNU General Public License for more details.
|
|
||||||
*
|
|
||||||
* You should have received a copy of the GNU General Public License
|
|
||||||
* along with this program; if not, write to:
|
|
||||||
* Free Software Foundation
|
|
||||||
* 51 Franklin Street, Fifth Floor
|
|
||||||
* Boston, MA 02111-1301 USA
|
|
||||||
*
|
|
||||||
*/
|
|
||||||
|
|
||||||
#include <linux/jiffies.h>
|
|
||||||
#include <linux/file.h>
|
|
||||||
#include <linux/slab.h>
|
|
||||||
#include <linux/stat.h>
|
|
||||||
#include <linux/sched.h>
|
|
||||||
#include <linux/fs.h>
|
|
||||||
#include <net/9p/9p.h>
|
|
||||||
|
|
||||||
#include "v9fs.h"
|
|
||||||
#include "cache.h"
|
|
||||||
|
|
||||||
#define CACHETAG_LEN 11
|
|
||||||
|
|
||||||
struct fscache_netfs v9fs_cache_netfs = {
|
|
||||||
.name = "9p",
|
|
||||||
.version = 0,
|
|
||||||
};
|
|
||||||
|
|
||||||
/**
|
|
||||||
* v9fs_random_cachetag - Generate a random tag to be associated
|
|
||||||
* with a new cache session.
|
|
||||||
*
|
|
||||||
* The value of jiffies is used for a fairly randomly cache tag.
|
|
||||||
*/
|
|
||||||
|
|
||||||
static
|
|
||||||
int v9fs_random_cachetag(struct v9fs_session_info *v9ses)
|
|
||||||
{
|
|
||||||
v9ses->cachetag = kmalloc(CACHETAG_LEN, GFP_KERNEL);
|
|
||||||
if (!v9ses->cachetag)
|
|
||||||
return -ENOMEM;
|
|
||||||
|
|
||||||
return scnprintf(v9ses->cachetag, CACHETAG_LEN, "%lu", jiffies);
|
|
||||||
}
|
|
||||||
|
|
||||||
static uint16_t v9fs_cache_session_get_key(const void *cookie_netfs_data,
|
|
||||||
void *buffer, uint16_t bufmax)
|
|
||||||
{
|
|
||||||
struct v9fs_session_info *v9ses;
|
|
||||||
uint16_t klen = 0;
|
|
||||||
|
|
||||||
v9ses = (struct v9fs_session_info *)cookie_netfs_data;
|
|
||||||
p9_debug(P9_DEBUG_FSC, "session %p buf %p size %u\n",
|
|
||||||
v9ses, buffer, bufmax);
|
|
||||||
|
|
||||||
if (v9ses->cachetag)
|
|
||||||
klen = strlen(v9ses->cachetag);
|
|
||||||
|
|
||||||
if (klen > bufmax)
|
|
||||||
return 0;
|
|
||||||
|
|
||||||
memcpy(buffer, v9ses->cachetag, klen);
|
|
||||||
p9_debug(P9_DEBUG_FSC, "cache session tag %s\n", v9ses->cachetag);
|
|
||||||
return klen;
|
|
||||||
}
|
|
||||||
|
|
||||||
const struct fscache_cookie_def v9fs_cache_session_index_def = {
|
|
||||||
.name = "9P.session",
|
|
||||||
.type = FSCACHE_COOKIE_TYPE_INDEX,
|
|
||||||
.get_key = v9fs_cache_session_get_key,
|
|
||||||
};
|
|
||||||
|
|
||||||
void v9fs_cache_session_get_cookie(struct v9fs_session_info *v9ses)
|
|
||||||
{
|
|
||||||
/* If no cache session tag was specified, we generate a random one. */
|
|
||||||
if (!v9ses->cachetag)
|
|
||||||
v9fs_random_cachetag(v9ses);
|
|
||||||
|
|
||||||
v9ses->fscache = fscache_acquire_cookie(v9fs_cache_netfs.primary_index,
|
|
||||||
&v9fs_cache_session_index_def,
|
|
||||||
v9ses, true);
|
|
||||||
p9_debug(P9_DEBUG_FSC, "session %p get cookie %p\n",
|
|
||||||
v9ses, v9ses->fscache);
|
|
||||||
}
|
|
||||||
|
|
||||||
void v9fs_cache_session_put_cookie(struct v9fs_session_info *v9ses)
|
|
||||||
{
|
|
||||||
p9_debug(P9_DEBUG_FSC, "session %p put cookie %p\n",
|
|
||||||
v9ses, v9ses->fscache);
|
|
||||||
fscache_relinquish_cookie(v9ses->fscache, 0);
|
|
||||||
v9ses->fscache = NULL;
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
static uint16_t v9fs_cache_inode_get_key(const void *cookie_netfs_data,
|
|
||||||
void *buffer, uint16_t bufmax)
|
|
||||||
{
|
|
||||||
const struct v9fs_inode *v9inode = cookie_netfs_data;
|
|
||||||
memcpy(buffer, &v9inode->qid.path, sizeof(v9inode->qid.path));
|
|
||||||
p9_debug(P9_DEBUG_FSC, "inode %p get key %llu\n",
|
|
||||||
&v9inode->vfs_inode, v9inode->qid.path);
|
|
||||||
return sizeof(v9inode->qid.path);
|
|
||||||
}
|
|
||||||
|
|
||||||
static void v9fs_cache_inode_get_attr(const void *cookie_netfs_data,
|
|
||||||
uint64_t *size)
|
|
||||||
{
|
|
||||||
const struct v9fs_inode *v9inode = cookie_netfs_data;
|
|
||||||
*size = i_size_read(&v9inode->vfs_inode);
|
|
||||||
|
|
||||||
p9_debug(P9_DEBUG_FSC, "inode %p get attr %llu\n",
|
|
||||||
&v9inode->vfs_inode, *size);
|
|
||||||
}
|
|
||||||
|
|
||||||
static uint16_t v9fs_cache_inode_get_aux(const void *cookie_netfs_data,
|
|
||||||
void *buffer, uint16_t buflen)
|
|
||||||
{
|
|
||||||
const struct v9fs_inode *v9inode = cookie_netfs_data;
|
|
||||||
memcpy(buffer, &v9inode->qid.version, sizeof(v9inode->qid.version));
|
|
||||||
p9_debug(P9_DEBUG_FSC, "inode %p get aux %u\n",
|
|
||||||
&v9inode->vfs_inode, v9inode->qid.version);
|
|
||||||
return sizeof(v9inode->qid.version);
|
|
||||||
}
|
|
||||||
|
|
||||||
static enum
|
|
||||||
fscache_checkaux v9fs_cache_inode_check_aux(void *cookie_netfs_data,
|
|
||||||
const void *buffer,
|
|
||||||
uint16_t buflen)
|
|
||||||
{
|
|
||||||
const struct v9fs_inode *v9inode = cookie_netfs_data;
|
|
||||||
|
|
||||||
if (buflen != sizeof(v9inode->qid.version))
|
|
||||||
return FSCACHE_CHECKAUX_OBSOLETE;
|
|
||||||
|
|
||||||
if (memcmp(buffer, &v9inode->qid.version,
|
|
||||||
sizeof(v9inode->qid.version)))
|
|
||||||
return FSCACHE_CHECKAUX_OBSOLETE;
|
|
||||||
|
|
||||||
return FSCACHE_CHECKAUX_OKAY;
|
|
||||||
}
|
|
||||||
|
|
||||||
static void v9fs_cache_inode_now_uncached(void *cookie_netfs_data)
|
|
||||||
{
|
|
||||||
struct v9fs_inode *v9inode = cookie_netfs_data;
|
|
||||||
struct pagevec pvec;
|
|
||||||
pgoff_t first;
|
|
||||||
int loop, nr_pages;
|
|
||||||
|
|
||||||
pagevec_init(&pvec, 0);
|
|
||||||
first = 0;
|
|
||||||
|
|
||||||
for (;;) {
|
|
||||||
nr_pages = pagevec_lookup(&pvec, v9inode->vfs_inode.i_mapping,
|
|
||||||
first,
|
|
||||||
PAGEVEC_SIZE - pagevec_count(&pvec));
|
|
||||||
if (!nr_pages)
|
|
||||||
break;
|
|
||||||
|
|
||||||
for (loop = 0; loop < nr_pages; loop++)
|
|
||||||
ClearPageFsCache(pvec.pages[loop]);
|
|
||||||
|
|
||||||
first = pvec.pages[nr_pages - 1]->index + 1;
|
|
||||||
|
|
||||||
pvec.nr = nr_pages;
|
|
||||||
pagevec_release(&pvec);
|
|
||||||
cond_resched();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
const struct fscache_cookie_def v9fs_cache_inode_index_def = {
|
|
||||||
.name = "9p.inode",
|
|
||||||
.type = FSCACHE_COOKIE_TYPE_DATAFILE,
|
|
||||||
.get_key = v9fs_cache_inode_get_key,
|
|
||||||
.get_attr = v9fs_cache_inode_get_attr,
|
|
||||||
.get_aux = v9fs_cache_inode_get_aux,
|
|
||||||
.check_aux = v9fs_cache_inode_check_aux,
|
|
||||||
.now_uncached = v9fs_cache_inode_now_uncached,
|
|
||||||
};
|
|
||||||
|
|
||||||
void v9fs_cache_inode_get_cookie(struct inode *inode)
|
|
||||||
{
|
|
||||||
struct v9fs_inode *v9inode;
|
|
||||||
struct v9fs_session_info *v9ses;
|
|
||||||
|
|
||||||
if (!S_ISREG(inode->i_mode))
|
|
||||||
return;
|
|
||||||
|
|
||||||
v9inode = V9FS_I(inode);
|
|
||||||
if (v9inode->fscache)
|
|
||||||
return;
|
|
||||||
|
|
||||||
v9ses = v9fs_inode2v9ses(inode);
|
|
||||||
v9inode->fscache = fscache_acquire_cookie(v9ses->fscache,
|
|
||||||
&v9fs_cache_inode_index_def,
|
|
||||||
v9inode, true);
|
|
||||||
|
|
||||||
p9_debug(P9_DEBUG_FSC, "inode %p get cookie %p\n",
|
|
||||||
inode, v9inode->fscache);
|
|
||||||
}
|
|
||||||
|
|
||||||
void v9fs_cache_inode_put_cookie(struct inode *inode)
|
|
||||||
{
|
|
||||||
struct v9fs_inode *v9inode = V9FS_I(inode);
|
|
||||||
|
|
||||||
if (!v9inode->fscache)
|
|
||||||
return;
|
|
||||||
p9_debug(P9_DEBUG_FSC, "inode %p put cookie %p\n",
|
|
||||||
inode, v9inode->fscache);
|
|
||||||
|
|
||||||
fscache_relinquish_cookie(v9inode->fscache, 0);
|
|
||||||
v9inode->fscache = NULL;
|
|
||||||
}
|
|
||||||
|
|
||||||
void v9fs_cache_inode_flush_cookie(struct inode *inode)
|
|
||||||
{
|
|
||||||
struct v9fs_inode *v9inode = V9FS_I(inode);
|
|
||||||
|
|
||||||
if (!v9inode->fscache)
|
|
||||||
return;
|
|
||||||
p9_debug(P9_DEBUG_FSC, "inode %p flush cookie %p\n",
|
|
||||||
inode, v9inode->fscache);
|
|
||||||
|
|
||||||
fscache_relinquish_cookie(v9inode->fscache, 1);
|
|
||||||
v9inode->fscache = NULL;
|
|
||||||
}
|
|
||||||
|
|
||||||
void v9fs_cache_inode_set_cookie(struct inode *inode, struct file *filp)
|
|
||||||
{
|
|
||||||
struct v9fs_inode *v9inode = V9FS_I(inode);
|
|
||||||
|
|
||||||
if (!v9inode->fscache)
|
|
||||||
return;
|
|
||||||
|
|
||||||
mutex_lock(&v9inode->fscache_lock);
|
|
||||||
|
|
||||||
if ((filp->f_flags & O_ACCMODE) != O_RDONLY)
|
|
||||||
v9fs_cache_inode_flush_cookie(inode);
|
|
||||||
else
|
|
||||||
v9fs_cache_inode_get_cookie(inode);
|
|
||||||
|
|
||||||
mutex_unlock(&v9inode->fscache_lock);
|
|
||||||
}
|
|
||||||
|
|
||||||
void v9fs_cache_inode_reset_cookie(struct inode *inode)
|
|
||||||
{
|
|
||||||
struct v9fs_inode *v9inode = V9FS_I(inode);
|
|
||||||
struct v9fs_session_info *v9ses;
|
|
||||||
struct fscache_cookie *old;
|
|
||||||
|
|
||||||
if (!v9inode->fscache)
|
|
||||||
return;
|
|
||||||
|
|
||||||
old = v9inode->fscache;
|
|
||||||
|
|
||||||
mutex_lock(&v9inode->fscache_lock);
|
|
||||||
fscache_relinquish_cookie(v9inode->fscache, 1);
|
|
||||||
|
|
||||||
v9ses = v9fs_inode2v9ses(inode);
|
|
||||||
v9inode->fscache = fscache_acquire_cookie(v9ses->fscache,
|
|
||||||
&v9fs_cache_inode_index_def,
|
|
||||||
v9inode, true);
|
|
||||||
p9_debug(P9_DEBUG_FSC, "inode %p revalidating cookie old %p new %p\n",
|
|
||||||
inode, old, v9inode->fscache);
|
|
||||||
|
|
||||||
mutex_unlock(&v9inode->fscache_lock);
|
|
||||||
}
|
|
||||||
|
|
||||||
int __v9fs_fscache_release_page(struct page *page, gfp_t gfp)
|
|
||||||
{
|
|
||||||
struct inode *inode = page->mapping->host;
|
|
||||||
struct v9fs_inode *v9inode = V9FS_I(inode);
|
|
||||||
|
|
||||||
BUG_ON(!v9inode->fscache);
|
|
||||||
|
|
||||||
return fscache_maybe_release_page(v9inode->fscache, page, gfp);
|
|
||||||
}
|
|
||||||
|
|
||||||
void __v9fs_fscache_invalidate_page(struct page *page)
|
|
||||||
{
|
|
||||||
struct inode *inode = page->mapping->host;
|
|
||||||
struct v9fs_inode *v9inode = V9FS_I(inode);
|
|
||||||
|
|
||||||
BUG_ON(!v9inode->fscache);
|
|
||||||
|
|
||||||
if (PageFsCache(page)) {
|
|
||||||
fscache_wait_on_page_write(v9inode->fscache, page);
|
|
||||||
BUG_ON(!PageLocked(page));
|
|
||||||
fscache_uncache_page(v9inode->fscache, page);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
static void v9fs_vfs_readpage_complete(struct page *page, void *data,
|
|
||||||
int error)
|
|
||||||
{
|
|
||||||
if (!error)
|
|
||||||
SetPageUptodate(page);
|
|
||||||
|
|
||||||
unlock_page(page);
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* __v9fs_readpage_from_fscache - read a page from cache
|
|
||||||
*
|
|
||||||
* Returns 0 if the pages are in cache and a BIO is submitted,
|
|
||||||
* 1 if the pages are not in cache and -error otherwise.
|
|
||||||
*/
|
|
||||||
|
|
||||||
int __v9fs_readpage_from_fscache(struct inode *inode, struct page *page)
|
|
||||||
{
|
|
||||||
int ret;
|
|
||||||
const struct v9fs_inode *v9inode = V9FS_I(inode);
|
|
||||||
|
|
||||||
p9_debug(P9_DEBUG_FSC, "inode %p page %p\n", inode, page);
|
|
||||||
if (!v9inode->fscache)
|
|
||||||
return -ENOBUFS;
|
|
||||||
|
|
||||||
ret = fscache_read_or_alloc_page(v9inode->fscache,
|
|
||||||
page,
|
|
||||||
v9fs_vfs_readpage_complete,
|
|
||||||
NULL,
|
|
||||||
GFP_KERNEL);
|
|
||||||
switch (ret) {
|
|
||||||
case -ENOBUFS:
|
|
||||||
case -ENODATA:
|
|
||||||
p9_debug(P9_DEBUG_FSC, "page/inode not in cache %d\n", ret);
|
|
||||||
return 1;
|
|
||||||
case 0:
|
|
||||||
p9_debug(P9_DEBUG_FSC, "BIO submitted\n");
|
|
||||||
return ret;
|
|
||||||
default:
|
|
||||||
p9_debug(P9_DEBUG_FSC, "ret %d\n", ret);
|
|
||||||
return ret;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* __v9fs_readpages_from_fscache - read multiple pages from cache
|
|
||||||
*
|
|
||||||
* Returns 0 if the pages are in cache and a BIO is submitted,
|
|
||||||
* 1 if the pages are not in cache and -error otherwise.
|
|
||||||
*/
|
|
||||||
|
|
||||||
int __v9fs_readpages_from_fscache(struct inode *inode,
|
|
||||||
struct address_space *mapping,
|
|
||||||
struct list_head *pages,
|
|
||||||
unsigned *nr_pages)
|
|
||||||
{
|
|
||||||
int ret;
|
|
||||||
const struct v9fs_inode *v9inode = V9FS_I(inode);
|
|
||||||
|
|
||||||
p9_debug(P9_DEBUG_FSC, "inode %p pages %u\n", inode, *nr_pages);
|
|
||||||
if (!v9inode->fscache)
|
|
||||||
return -ENOBUFS;
|
|
||||||
|
|
||||||
ret = fscache_read_or_alloc_pages(v9inode->fscache,
|
|
||||||
mapping, pages, nr_pages,
|
|
||||||
v9fs_vfs_readpage_complete,
|
|
||||||
NULL,
|
|
||||||
mapping_gfp_mask(mapping));
|
|
||||||
switch (ret) {
|
|
||||||
case -ENOBUFS:
|
|
||||||
case -ENODATA:
|
|
||||||
p9_debug(P9_DEBUG_FSC, "pages/inodes not in cache %d\n", ret);
|
|
||||||
return 1;
|
|
||||||
case 0:
|
|
||||||
BUG_ON(!list_empty(pages));
|
|
||||||
BUG_ON(*nr_pages != 0);
|
|
||||||
p9_debug(P9_DEBUG_FSC, "BIO submitted\n");
|
|
||||||
return ret;
|
|
||||||
default:
|
|
||||||
p9_debug(P9_DEBUG_FSC, "ret %d\n", ret);
|
|
||||||
return ret;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* __v9fs_readpage_to_fscache - write a page to the cache
|
|
||||||
*
|
|
||||||
*/
|
|
||||||
|
|
||||||
void __v9fs_readpage_to_fscache(struct inode *inode, struct page *page)
|
|
||||||
{
|
|
||||||
int ret;
|
|
||||||
const struct v9fs_inode *v9inode = V9FS_I(inode);
|
|
||||||
|
|
||||||
p9_debug(P9_DEBUG_FSC, "inode %p page %p\n", inode, page);
|
|
||||||
ret = fscache_write_page(v9inode->fscache, page, GFP_KERNEL);
|
|
||||||
p9_debug(P9_DEBUG_FSC, "ret = %d\n", ret);
|
|
||||||
if (ret != 0)
|
|
||||||
v9fs_uncache_page(inode, page);
|
|
||||||
}
|
|
||||||
|
|
||||||
/*
|
|
||||||
* wait for a page to complete writing to the cache
|
|
||||||
*/
|
|
||||||
void __v9fs_fscache_wait_on_page_write(struct inode *inode, struct page *page)
|
|
||||||
{
|
|
||||||
const struct v9fs_inode *v9inode = V9FS_I(inode);
|
|
||||||
p9_debug(P9_DEBUG_FSC, "inode %p page %p\n", inode, page);
|
|
||||||
if (PageFsCache(page))
|
|
||||||
fscache_wait_on_page_write(v9inode->fscache, page);
|
|
||||||
}
|
|
@ -1,152 +0,0 @@
|
|||||||
/*
|
|
||||||
* V9FS cache definitions.
|
|
||||||
*
|
|
||||||
* Copyright (C) 2009 by Abhishek Kulkarni <adkulkar@umail.iu.edu>
|
|
||||||
*
|
|
||||||
* This program is free software; you can redistribute it and/or modify
|
|
||||||
* it under the terms of the GNU General Public License version 2
|
|
||||||
* as published by the Free Software Foundation.
|
|
||||||
*
|
|
||||||
* This program is distributed in the hope that it will be useful,
|
|
||||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
||||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
||||||
* GNU General Public License for more details.
|
|
||||||
*
|
|
||||||
* You should have received a copy of the GNU General Public License
|
|
||||||
* along with this program; if not, write to:
|
|
||||||
* Free Software Foundation
|
|
||||||
* 51 Franklin Street, Fifth Floor
|
|
||||||
* Boston, MA 02111-1301 USA
|
|
||||||
*
|
|
||||||
*/
|
|
||||||
|
|
||||||
#ifndef _9P_CACHE_H
|
|
||||||
#define _9P_CACHE_H
|
|
||||||
#ifdef CONFIG_9P_FSCACHE
|
|
||||||
#include <linux/fscache.h>
|
|
||||||
#include <linux/spinlock.h>
|
|
||||||
|
|
||||||
extern struct fscache_netfs v9fs_cache_netfs;
|
|
||||||
extern const struct fscache_cookie_def v9fs_cache_session_index_def;
|
|
||||||
extern const struct fscache_cookie_def v9fs_cache_inode_index_def;
|
|
||||||
|
|
||||||
extern void v9fs_cache_session_get_cookie(struct v9fs_session_info *v9ses);
|
|
||||||
extern void v9fs_cache_session_put_cookie(struct v9fs_session_info *v9ses);
|
|
||||||
|
|
||||||
extern void v9fs_cache_inode_get_cookie(struct inode *inode);
|
|
||||||
extern void v9fs_cache_inode_put_cookie(struct inode *inode);
|
|
||||||
extern void v9fs_cache_inode_flush_cookie(struct inode *inode);
|
|
||||||
extern void v9fs_cache_inode_set_cookie(struct inode *inode, struct file *filp);
|
|
||||||
extern void v9fs_cache_inode_reset_cookie(struct inode *inode);
|
|
||||||
|
|
||||||
extern int __v9fs_cache_register(void);
|
|
||||||
extern void __v9fs_cache_unregister(void);
|
|
||||||
|
|
||||||
extern int __v9fs_fscache_release_page(struct page *page, gfp_t gfp);
|
|
||||||
extern void __v9fs_fscache_invalidate_page(struct page *page);
|
|
||||||
extern int __v9fs_readpage_from_fscache(struct inode *inode,
|
|
||||||
struct page *page);
|
|
||||||
extern int __v9fs_readpages_from_fscache(struct inode *inode,
|
|
||||||
struct address_space *mapping,
|
|
||||||
struct list_head *pages,
|
|
||||||
unsigned *nr_pages);
|
|
||||||
extern void __v9fs_readpage_to_fscache(struct inode *inode, struct page *page);
|
|
||||||
extern void __v9fs_fscache_wait_on_page_write(struct inode *inode,
|
|
||||||
struct page *page);
|
|
||||||
|
|
||||||
static inline int v9fs_fscache_release_page(struct page *page,
|
|
||||||
gfp_t gfp)
|
|
||||||
{
|
|
||||||
return __v9fs_fscache_release_page(page, gfp);
|
|
||||||
}
|
|
||||||
|
|
||||||
static inline void v9fs_fscache_invalidate_page(struct page *page)
|
|
||||||
{
|
|
||||||
__v9fs_fscache_invalidate_page(page);
|
|
||||||
}
|
|
||||||
|
|
||||||
static inline int v9fs_readpage_from_fscache(struct inode *inode,
|
|
||||||
struct page *page)
|
|
||||||
{
|
|
||||||
return __v9fs_readpage_from_fscache(inode, page);
|
|
||||||
}
|
|
||||||
|
|
||||||
static inline int v9fs_readpages_from_fscache(struct inode *inode,
|
|
||||||
struct address_space *mapping,
|
|
||||||
struct list_head *pages,
|
|
||||||
unsigned *nr_pages)
|
|
||||||
{
|
|
||||||
return __v9fs_readpages_from_fscache(inode, mapping, pages,
|
|
||||||
nr_pages);
|
|
||||||
}
|
|
||||||
|
|
||||||
static inline void v9fs_readpage_to_fscache(struct inode *inode,
|
|
||||||
struct page *page)
|
|
||||||
{
|
|
||||||
if (PageFsCache(page))
|
|
||||||
__v9fs_readpage_to_fscache(inode, page);
|
|
||||||
}
|
|
||||||
|
|
||||||
static inline void v9fs_uncache_page(struct inode *inode, struct page *page)
|
|
||||||
{
|
|
||||||
struct v9fs_inode *v9inode = V9FS_I(inode);
|
|
||||||
fscache_uncache_page(v9inode->fscache, page);
|
|
||||||
BUG_ON(PageFsCache(page));
|
|
||||||
}
|
|
||||||
|
|
||||||
static inline void v9fs_fscache_wait_on_page_write(struct inode *inode,
|
|
||||||
struct page *page)
|
|
||||||
{
|
|
||||||
return __v9fs_fscache_wait_on_page_write(inode, page);
|
|
||||||
}
|
|
||||||
|
|
||||||
#else /* CONFIG_9P_FSCACHE */
|
|
||||||
|
|
||||||
static inline void v9fs_cache_inode_get_cookie(struct inode *inode)
|
|
||||||
{
|
|
||||||
}
|
|
||||||
|
|
||||||
static inline void v9fs_cache_inode_put_cookie(struct inode *inode)
|
|
||||||
{
|
|
||||||
}
|
|
||||||
|
|
||||||
static inline void v9fs_cache_inode_set_cookie(struct inode *inode, struct file *file)
|
|
||||||
{
|
|
||||||
}
|
|
||||||
|
|
||||||
static inline int v9fs_fscache_release_page(struct page *page,
|
|
||||||
gfp_t gfp) {
|
|
||||||
return 1;
|
|
||||||
}
|
|
||||||
|
|
||||||
static inline void v9fs_fscache_invalidate_page(struct page *page) {}
|
|
||||||
|
|
||||||
static inline int v9fs_readpage_from_fscache(struct inode *inode,
|
|
||||||
struct page *page)
|
|
||||||
{
|
|
||||||
return -ENOBUFS;
|
|
||||||
}
|
|
||||||
|
|
||||||
static inline int v9fs_readpages_from_fscache(struct inode *inode,
|
|
||||||
struct address_space *mapping,
|
|
||||||
struct list_head *pages,
|
|
||||||
unsigned *nr_pages)
|
|
||||||
{
|
|
||||||
return -ENOBUFS;
|
|
||||||
}
|
|
||||||
|
|
||||||
static inline void v9fs_readpage_to_fscache(struct inode *inode,
|
|
||||||
struct page *page)
|
|
||||||
{}
|
|
||||||
|
|
||||||
static inline void v9fs_uncache_page(struct inode *inode, struct page *page)
|
|
||||||
{}
|
|
||||||
|
|
||||||
static inline void v9fs_fscache_wait_on_page_write(struct inode *inode,
|
|
||||||
struct page *page)
|
|
||||||
{
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
#endif /* CONFIG_9P_FSCACHE */
|
|
||||||
#endif /* _9P_CACHE_H */
|
|
File diff suppressed because it is too large
Load Diff
@ -1,247 +0,0 @@
|
|||||||
/*
|
|
||||||
* linux/fs/9p/error.c
|
|
||||||
*
|
|
||||||
* Error string handling
|
|
||||||
*
|
|
||||||
* Plan 9 uses error strings, Unix uses error numbers. These functions
|
|
||||||
* try to help manage that and provide for dynamically adding error
|
|
||||||
* mappings.
|
|
||||||
*
|
|
||||||
* Copyright (C) 2004 by Eric Van Hensbergen <ericvh@gmail.com>
|
|
||||||
* Copyright (C) 2002 by Ron Minnich <rminnich@lanl.gov>
|
|
||||||
*
|
|
||||||
* This program is free software; you can redistribute it and/or modify
|
|
||||||
* it under the terms of the GNU General Public License version 2
|
|
||||||
* as published by the Free Software Foundation.
|
|
||||||
*
|
|
||||||
* This program is distributed in the hope that it will be useful,
|
|
||||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
||||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
||||||
* GNU General Public License for more details.
|
|
||||||
*
|
|
||||||
* You should have received a copy of the GNU General Public License
|
|
||||||
* along with this program; if not, write to:
|
|
||||||
* Free Software Foundation
|
|
||||||
* 51 Franklin Street, Fifth Floor
|
|
||||||
* Boston, MA 02111-1301 USA
|
|
||||||
*
|
|
||||||
*/
|
|
||||||
|
|
||||||
#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
|
|
||||||
|
|
||||||
#include <linux/module.h>
|
|
||||||
#include <linux/list.h>
|
|
||||||
#include <linux/jhash.h>
|
|
||||||
#include <linux/errno.h>
|
|
||||||
#include <net/9p/9p.h>
|
|
||||||
|
|
||||||
/**
|
|
||||||
* struct errormap - map string errors from Plan 9 to Linux numeric ids
|
|
||||||
* @name: string sent over 9P
|
|
||||||
* @val: numeric id most closely representing @name
|
|
||||||
* @namelen: length of string
|
|
||||||
* @list: hash-table list for string lookup
|
|
||||||
*/
|
|
||||||
struct errormap {
|
|
||||||
char *name;
|
|
||||||
int val;
|
|
||||||
|
|
||||||
int namelen;
|
|
||||||
struct hlist_node list;
|
|
||||||
};
|
|
||||||
|
|
||||||
#define ERRHASHSZ 32
|
|
||||||
static struct hlist_head hash_errmap[ERRHASHSZ];
|
|
||||||
|
|
||||||
/* FixMe - reduce to a reasonable size */
|
|
||||||
static struct errormap errmap[] = {
|
|
||||||
{"Operation not permitted", EPERM},
|
|
||||||
{"wstat prohibited", EPERM},
|
|
||||||
{"No such file or directory", ENOENT},
|
|
||||||
{"directory entry not found", ENOENT},
|
|
||||||
{"file not found", ENOENT},
|
|
||||||
{"Interrupted system call", EINTR},
|
|
||||||
{"Input/output error", EIO},
|
|
||||||
{"No such device or address", ENXIO},
|
|
||||||
{"Argument list too long", E2BIG},
|
|
||||||
{"Bad file descriptor", EBADF},
|
|
||||||
{"Resource temporarily unavailable", EAGAIN},
|
|
||||||
{"Cannot allocate memory", ENOMEM},
|
|
||||||
{"Permission denied", EACCES},
|
|
||||||
{"Bad address", EFAULT},
|
|
||||||
{"Block device required", ENOTBLK},
|
|
||||||
{"Device or resource busy", EBUSY},
|
|
||||||
{"File exists", EEXIST},
|
|
||||||
{"Invalid cross-device link", EXDEV},
|
|
||||||
{"No such device", ENODEV},
|
|
||||||
{"Not a directory", ENOTDIR},
|
|
||||||
{"Is a directory", EISDIR},
|
|
||||||
{"Invalid argument", EINVAL},
|
|
||||||
{"Too many open files in system", ENFILE},
|
|
||||||
{"Too many open files", EMFILE},
|
|
||||||
{"Text file busy", ETXTBSY},
|
|
||||||
{"File too large", EFBIG},
|
|
||||||
{"No space left on device", ENOSPC},
|
|
||||||
{"Illegal seek", ESPIPE},
|
|
||||||
{"Read-only file system", EROFS},
|
|
||||||
{"Too many links", EMLINK},
|
|
||||||
{"Broken pipe", EPIPE},
|
|
||||||
{"Numerical argument out of domain", EDOM},
|
|
||||||
{"Numerical result out of range", ERANGE},
|
|
||||||
{"Resource deadlock avoided", EDEADLK},
|
|
||||||
{"File name too long", ENAMETOOLONG},
|
|
||||||
{"No locks available", ENOLCK},
|
|
||||||
{"Function not implemented", ENOSYS},
|
|
||||||
{"Directory not empty", ENOTEMPTY},
|
|
||||||
{"Too many levels of symbolic links", ELOOP},
|
|
||||||
{"No message of desired type", ENOMSG},
|
|
||||||
{"Identifier removed", EIDRM},
|
|
||||||
{"No data available", ENODATA},
|
|
||||||
{"Machine is not on the network", ENONET},
|
|
||||||
{"Package not installed", ENOPKG},
|
|
||||||
{"Object is remote", EREMOTE},
|
|
||||||
{"Link has been severed", ENOLINK},
|
|
||||||
{"Communication error on send", ECOMM},
|
|
||||||
{"Protocol error", EPROTO},
|
|
||||||
{"Bad message", EBADMSG},
|
|
||||||
{"File descriptor in bad state", EBADFD},
|
|
||||||
{"Streams pipe error", ESTRPIPE},
|
|
||||||
{"Too many users", EUSERS},
|
|
||||||
{"Socket operation on non-socket", ENOTSOCK},
|
|
||||||
{"Message too long", EMSGSIZE},
|
|
||||||
{"Protocol not available", ENOPROTOOPT},
|
|
||||||
{"Protocol not supported", EPROTONOSUPPORT},
|
|
||||||
{"Socket type not supported", ESOCKTNOSUPPORT},
|
|
||||||
{"Operation not supported", EOPNOTSUPP},
|
|
||||||
{"Protocol family not supported", EPFNOSUPPORT},
|
|
||||||
{"Network is down", ENETDOWN},
|
|
||||||
{"Network is unreachable", ENETUNREACH},
|
|
||||||
{"Network dropped connection on reset", ENETRESET},
|
|
||||||
{"Software caused connection abort", ECONNABORTED},
|
|
||||||
{"Connection reset by peer", ECONNRESET},
|
|
||||||
{"No buffer space available", ENOBUFS},
|
|
||||||
{"Transport endpoint is already connected", EISCONN},
|
|
||||||
{"Transport endpoint is not connected", ENOTCONN},
|
|
||||||
{"Cannot send after transport endpoint shutdown", ESHUTDOWN},
|
|
||||||
{"Connection timed out", ETIMEDOUT},
|
|
||||||
{"Connection refused", ECONNREFUSED},
|
|
||||||
{"Host is down", EHOSTDOWN},
|
|
||||||
{"No route to host", EHOSTUNREACH},
|
|
||||||
{"Operation already in progress", EALREADY},
|
|
||||||
{"Operation now in progress", EINPROGRESS},
|
|
||||||
{"Is a named type file", EISNAM},
|
|
||||||
{"Remote I/O error", EREMOTEIO},
|
|
||||||
{"Disk quota exceeded", EDQUOT},
|
|
||||||
/* errors from fossil, vacfs, and u9fs */
|
|
||||||
{"fid unknown or out of range", EBADF},
|
|
||||||
{"permission denied", EACCES},
|
|
||||||
{"file does not exist", ENOENT},
|
|
||||||
{"authentication failed", ECONNREFUSED},
|
|
||||||
{"bad offset in directory read", ESPIPE},
|
|
||||||
{"bad use of fid", EBADF},
|
|
||||||
{"wstat can't convert between files and directories", EPERM},
|
|
||||||
{"directory is not empty", ENOTEMPTY},
|
|
||||||
{"file exists", EEXIST},
|
|
||||||
{"file already exists", EEXIST},
|
|
||||||
{"file or directory already exists", EEXIST},
|
|
||||||
{"fid already in use", EBADF},
|
|
||||||
{"file in use", ETXTBSY},
|
|
||||||
{"i/o error", EIO},
|
|
||||||
{"file already open for I/O", ETXTBSY},
|
|
||||||
{"illegal mode", EINVAL},
|
|
||||||
{"illegal name", ENAMETOOLONG},
|
|
||||||
{"not a directory", ENOTDIR},
|
|
||||||
{"not a member of proposed group", EPERM},
|
|
||||||
{"not owner", EACCES},
|
|
||||||
{"only owner can change group in wstat", EACCES},
|
|
||||||
{"read only file system", EROFS},
|
|
||||||
{"no access to special file", EPERM},
|
|
||||||
{"i/o count too large", EIO},
|
|
||||||
{"unknown group", EINVAL},
|
|
||||||
{"unknown user", EINVAL},
|
|
||||||
{"bogus wstat buffer", EPROTO},
|
|
||||||
{"exclusive use file already open", EAGAIN},
|
|
||||||
{"corrupted directory entry", EIO},
|
|
||||||
{"corrupted file entry", EIO},
|
|
||||||
{"corrupted block label", EIO},
|
|
||||||
{"corrupted meta data", EIO},
|
|
||||||
{"illegal offset", EINVAL},
|
|
||||||
{"illegal path element", ENOENT},
|
|
||||||
{"root of file system is corrupted", EIO},
|
|
||||||
{"corrupted super block", EIO},
|
|
||||||
{"protocol botch", EPROTO},
|
|
||||||
{"file system is full", ENOSPC},
|
|
||||||
{"file is in use", EAGAIN},
|
|
||||||
{"directory entry is not allocated", ENOENT},
|
|
||||||
{"file is read only", EROFS},
|
|
||||||
{"file has been removed", EIDRM},
|
|
||||||
{"only support truncation to zero length", EPERM},
|
|
||||||
{"cannot remove root", EPERM},
|
|
||||||
{"file too big", EFBIG},
|
|
||||||
{"venti i/o error", EIO},
|
|
||||||
/* these are not errors */
|
|
||||||
{"u9fs rhostsauth: no authentication required", 0},
|
|
||||||
{"u9fs authnone: no authentication required", 0},
|
|
||||||
{NULL, -1}
|
|
||||||
};
|
|
||||||
|
|
||||||
/**
|
|
||||||
* p9_error_init - preload mappings into hash list
|
|
||||||
*
|
|
||||||
*/
|
|
||||||
|
|
||||||
int p9_error_init(void)
|
|
||||||
{
|
|
||||||
struct errormap *c;
|
|
||||||
int bucket;
|
|
||||||
|
|
||||||
/* initialize hash table */
|
|
||||||
for (bucket = 0; bucket < ERRHASHSZ; bucket++)
|
|
||||||
INIT_HLIST_HEAD(&hash_errmap[bucket]);
|
|
||||||
|
|
||||||
/* load initial error map into hash table */
|
|
||||||
for (c = errmap; c->name != NULL; c++) {
|
|
||||||
c->namelen = strlen(c->name);
|
|
||||||
bucket = jhash(c->name, c->namelen, 0) % ERRHASHSZ;
|
|
||||||
INIT_HLIST_NODE(&c->list);
|
|
||||||
hlist_add_head(&c->list, &hash_errmap[bucket]);
|
|
||||||
}
|
|
||||||
|
|
||||||
return 1;
|
|
||||||
}
|
|
||||||
EXPORT_SYMBOL(p9_error_init);
|
|
||||||
|
|
||||||
/**
|
|
||||||
* errstr2errno - convert error string to error number
|
|
||||||
* @errstr: error string
|
|
||||||
* @len: length of error string
|
|
||||||
*
|
|
||||||
*/
|
|
||||||
|
|
||||||
int p9_errstr2errno(char *errstr, int len)
|
|
||||||
{
|
|
||||||
int errno;
|
|
||||||
struct errormap *c;
|
|
||||||
int bucket;
|
|
||||||
|
|
||||||
errno = 0;
|
|
||||||
c = NULL;
|
|
||||||
bucket = jhash(errstr, len, 0) % ERRHASHSZ;
|
|
||||||
hlist_for_each_entry(c, &hash_errmap[bucket], list) {
|
|
||||||
if (c->namelen == len && !memcmp(c->name, errstr, len)) {
|
|
||||||
errno = c->val;
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if (errno == 0) {
|
|
||||||
/* TODO: if error isn't found, add it dynamically */
|
|
||||||
errstr[len] = 0;
|
|
||||||
pr_err("%s: server reported unknown error %s\n",
|
|
||||||
__func__, errstr);
|
|
||||||
errno = ESERVERFAULT;
|
|
||||||
}
|
|
||||||
|
|
||||||
return -errno;
|
|
||||||
}
|
|
||||||
EXPORT_SYMBOL(p9_errstr2errno);
|
|
@ -1,306 +0,0 @@
|
|||||||
/*
|
|
||||||
* V9FS FID Management
|
|
||||||
*
|
|
||||||
* Copyright (C) 2007 by Latchesar Ionkov <lucho@ionkov.net>
|
|
||||||
* Copyright (C) 2005, 2006 by Eric Van Hensbergen <ericvh@gmail.com>
|
|
||||||
*
|
|
||||||
* This program is free software; you can redistribute it and/or modify
|
|
||||||
* it under the terms of the GNU General Public License version 2
|
|
||||||
* as published by the Free Software Foundation.
|
|
||||||
*
|
|
||||||
* This program is distributed in the hope that it will be useful,
|
|
||||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
||||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
||||||
* GNU General Public License for more details.
|
|
||||||
*
|
|
||||||
* You should have received a copy of the GNU General Public License
|
|
||||||
* along with this program; if not, write to:
|
|
||||||
* Free Software Foundation
|
|
||||||
* 51 Franklin Street, Fifth Floor
|
|
||||||
* Boston, MA 02111-1301 USA
|
|
||||||
*
|
|
||||||
*/
|
|
||||||
|
|
||||||
#include <linux/module.h>
|
|
||||||
#include <linux/errno.h>
|
|
||||||
#include <linux/fs.h>
|
|
||||||
#include <linux/slab.h>
|
|
||||||
#include <linux/sched.h>
|
|
||||||
#include <linux/idr.h>
|
|
||||||
#include <net/9p/9p.h>
|
|
||||||
#include <net/9p/client.h>
|
|
||||||
|
|
||||||
#include "v9fs.h"
|
|
||||||
#include "v9fs_vfs.h"
|
|
||||||
#include "fid.h"
|
|
||||||
|
|
||||||
/**
|
|
||||||
* v9fs_fid_add - add a fid to a dentry
|
|
||||||
* @dentry: dentry that the fid is being added to
|
|
||||||
* @fid: fid to add
|
|
||||||
*
|
|
||||||
*/
|
|
||||||
|
|
||||||
static inline void __add_fid(struct dentry *dentry, struct p9_fid *fid)
|
|
||||||
{
|
|
||||||
hlist_add_head(&fid->dlist, (struct hlist_head *)&dentry->d_fsdata);
|
|
||||||
}
|
|
||||||
|
|
||||||
void v9fs_fid_add(struct dentry *dentry, struct p9_fid *fid)
|
|
||||||
{
|
|
||||||
spin_lock(&dentry->d_lock);
|
|
||||||
__add_fid(dentry, fid);
|
|
||||||
spin_unlock(&dentry->d_lock);
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* v9fs_fid_find - retrieve a fid that belongs to the specified uid
|
|
||||||
* @dentry: dentry to look for fid in
|
|
||||||
* @uid: return fid that belongs to the specified user
|
|
||||||
* @any: if non-zero, return any fid associated with the dentry
|
|
||||||
*
|
|
||||||
*/
|
|
||||||
|
|
||||||
static struct p9_fid *v9fs_fid_find(struct dentry *dentry, kuid_t uid, int any)
|
|
||||||
{
|
|
||||||
struct p9_fid *fid, *ret;
|
|
||||||
|
|
||||||
p9_debug(P9_DEBUG_VFS, " dentry: %pd (%p) uid %d any %d\n",
|
|
||||||
dentry, dentry, from_kuid(&init_user_ns, uid),
|
|
||||||
any);
|
|
||||||
ret = NULL;
|
|
||||||
/* we'll recheck under lock if there's anything to look in */
|
|
||||||
if (dentry->d_fsdata) {
|
|
||||||
struct hlist_head *h = (struct hlist_head *)&dentry->d_fsdata;
|
|
||||||
spin_lock(&dentry->d_lock);
|
|
||||||
hlist_for_each_entry(fid, h, dlist) {
|
|
||||||
if (any || uid_eq(fid->uid, uid)) {
|
|
||||||
ret = fid;
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
spin_unlock(&dentry->d_lock);
|
|
||||||
}
|
|
||||||
|
|
||||||
return ret;
|
|
||||||
}
|
|
||||||
|
|
||||||
/*
|
|
||||||
* We need to hold v9ses->rename_sem as long as we hold references
|
|
||||||
* to returned path array. Array element contain pointers to
|
|
||||||
* dentry names.
|
|
||||||
*/
|
|
||||||
static int build_path_from_dentry(struct v9fs_session_info *v9ses,
|
|
||||||
struct dentry *dentry, char ***names)
|
|
||||||
{
|
|
||||||
int n = 0, i;
|
|
||||||
char **wnames;
|
|
||||||
struct dentry *ds;
|
|
||||||
|
|
||||||
for (ds = dentry; !IS_ROOT(ds); ds = ds->d_parent)
|
|
||||||
n++;
|
|
||||||
|
|
||||||
wnames = kmalloc(sizeof(char *) * n, GFP_KERNEL);
|
|
||||||
if (!wnames)
|
|
||||||
goto err_out;
|
|
||||||
|
|
||||||
for (ds = dentry, i = (n-1); i >= 0; i--, ds = ds->d_parent)
|
|
||||||
wnames[i] = (char *)ds->d_name.name;
|
|
||||||
|
|
||||||
*names = wnames;
|
|
||||||
return n;
|
|
||||||
err_out:
|
|
||||||
return -ENOMEM;
|
|
||||||
}
|
|
||||||
|
|
||||||
static struct p9_fid *v9fs_fid_lookup_with_uid(struct dentry *dentry,
|
|
||||||
kuid_t uid, int any)
|
|
||||||
{
|
|
||||||
struct dentry *ds;
|
|
||||||
char **wnames, *uname;
|
|
||||||
int i, n, l, clone, access;
|
|
||||||
struct v9fs_session_info *v9ses;
|
|
||||||
struct p9_fid *fid, *old_fid = NULL;
|
|
||||||
|
|
||||||
v9ses = v9fs_dentry2v9ses(dentry);
|
|
||||||
access = v9ses->flags & V9FS_ACCESS_MASK;
|
|
||||||
fid = v9fs_fid_find(dentry, uid, any);
|
|
||||||
if (fid)
|
|
||||||
return fid;
|
|
||||||
/*
|
|
||||||
* we don't have a matching fid. To do a TWALK we need
|
|
||||||
* parent fid. We need to prevent rename when we want to
|
|
||||||
* look at the parent.
|
|
||||||
*/
|
|
||||||
down_read(&v9ses->rename_sem);
|
|
||||||
ds = dentry->d_parent;
|
|
||||||
fid = v9fs_fid_find(ds, uid, any);
|
|
||||||
if (fid) {
|
|
||||||
/* Found the parent fid do a lookup with that */
|
|
||||||
fid = p9_client_walk(fid, 1, (char **)&dentry->d_name.name, 1);
|
|
||||||
goto fid_out;
|
|
||||||
}
|
|
||||||
up_read(&v9ses->rename_sem);
|
|
||||||
|
|
||||||
/* start from the root and try to do a lookup */
|
|
||||||
fid = v9fs_fid_find(dentry->d_sb->s_root, uid, any);
|
|
||||||
if (!fid) {
|
|
||||||
/* the user is not attached to the fs yet */
|
|
||||||
if (access == V9FS_ACCESS_SINGLE)
|
|
||||||
return ERR_PTR(-EPERM);
|
|
||||||
|
|
||||||
if (v9fs_proto_dotu(v9ses) || v9fs_proto_dotl(v9ses))
|
|
||||||
uname = NULL;
|
|
||||||
else
|
|
||||||
uname = v9ses->uname;
|
|
||||||
|
|
||||||
fid = p9_client_attach(v9ses->clnt, NULL, uname, uid,
|
|
||||||
v9ses->aname);
|
|
||||||
if (IS_ERR(fid))
|
|
||||||
return fid;
|
|
||||||
|
|
||||||
v9fs_fid_add(dentry->d_sb->s_root, fid);
|
|
||||||
}
|
|
||||||
/* If we are root ourself just return that */
|
|
||||||
if (dentry->d_sb->s_root == dentry)
|
|
||||||
return fid;
|
|
||||||
/*
|
|
||||||
* Do a multipath walk with attached root.
|
|
||||||
* When walking parent we need to make sure we
|
|
||||||
* don't have a parallel rename happening
|
|
||||||
*/
|
|
||||||
down_read(&v9ses->rename_sem);
|
|
||||||
n = build_path_from_dentry(v9ses, dentry, &wnames);
|
|
||||||
if (n < 0) {
|
|
||||||
fid = ERR_PTR(n);
|
|
||||||
goto err_out;
|
|
||||||
}
|
|
||||||
clone = 1;
|
|
||||||
i = 0;
|
|
||||||
while (i < n) {
|
|
||||||
l = min(n - i, P9_MAXWELEM);
|
|
||||||
/*
|
|
||||||
* We need to hold rename lock when doing a multipath
|
|
||||||
* walk to ensure none of the patch component change
|
|
||||||
*/
|
|
||||||
fid = p9_client_walk(fid, l, &wnames[i], clone);
|
|
||||||
if (IS_ERR(fid)) {
|
|
||||||
if (old_fid) {
|
|
||||||
/*
|
|
||||||
* If we fail, clunk fid which are mapping
|
|
||||||
* to path component and not the last component
|
|
||||||
* of the path.
|
|
||||||
*/
|
|
||||||
p9_client_clunk(old_fid);
|
|
||||||
}
|
|
||||||
kfree(wnames);
|
|
||||||
goto err_out;
|
|
||||||
}
|
|
||||||
old_fid = fid;
|
|
||||||
i += l;
|
|
||||||
clone = 0;
|
|
||||||
}
|
|
||||||
kfree(wnames);
|
|
||||||
fid_out:
|
|
||||||
if (!IS_ERR(fid)) {
|
|
||||||
spin_lock(&dentry->d_lock);
|
|
||||||
if (d_unhashed(dentry)) {
|
|
||||||
spin_unlock(&dentry->d_lock);
|
|
||||||
p9_client_clunk(fid);
|
|
||||||
fid = ERR_PTR(-ENOENT);
|
|
||||||
} else {
|
|
||||||
__add_fid(dentry, fid);
|
|
||||||
spin_unlock(&dentry->d_lock);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
err_out:
|
|
||||||
up_read(&v9ses->rename_sem);
|
|
||||||
return fid;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* v9fs_fid_lookup - lookup for a fid, try to walk if not found
|
|
||||||
* @dentry: dentry to look for fid in
|
|
||||||
*
|
|
||||||
* Look for a fid in the specified dentry for the current user.
|
|
||||||
* If no fid is found, try to create one walking from a fid from the parent
|
|
||||||
* dentry (if it has one), or the root dentry. If the user haven't accessed
|
|
||||||
* the fs yet, attach now and walk from the root.
|
|
||||||
*/
|
|
||||||
|
|
||||||
struct p9_fid *v9fs_fid_lookup(struct dentry *dentry)
|
|
||||||
{
|
|
||||||
kuid_t uid;
|
|
||||||
int any, access;
|
|
||||||
struct v9fs_session_info *v9ses;
|
|
||||||
|
|
||||||
v9ses = v9fs_dentry2v9ses(dentry);
|
|
||||||
access = v9ses->flags & V9FS_ACCESS_MASK;
|
|
||||||
switch (access) {
|
|
||||||
case V9FS_ACCESS_SINGLE:
|
|
||||||
case V9FS_ACCESS_USER:
|
|
||||||
case V9FS_ACCESS_CLIENT:
|
|
||||||
uid = current_fsuid();
|
|
||||||
any = 0;
|
|
||||||
break;
|
|
||||||
|
|
||||||
case V9FS_ACCESS_ANY:
|
|
||||||
uid = v9ses->uid;
|
|
||||||
any = 1;
|
|
||||||
break;
|
|
||||||
|
|
||||||
default:
|
|
||||||
uid = INVALID_UID;
|
|
||||||
any = 0;
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
return v9fs_fid_lookup_with_uid(dentry, uid, any);
|
|
||||||
}
|
|
||||||
|
|
||||||
struct p9_fid *v9fs_fid_clone(struct dentry *dentry)
|
|
||||||
{
|
|
||||||
struct p9_fid *fid, *ret;
|
|
||||||
|
|
||||||
fid = v9fs_fid_lookup(dentry);
|
|
||||||
if (IS_ERR(fid))
|
|
||||||
return fid;
|
|
||||||
|
|
||||||
ret = p9_client_walk(fid, 0, NULL, 1);
|
|
||||||
return ret;
|
|
||||||
}
|
|
||||||
|
|
||||||
static struct p9_fid *v9fs_fid_clone_with_uid(struct dentry *dentry, kuid_t uid)
|
|
||||||
{
|
|
||||||
struct p9_fid *fid, *ret;
|
|
||||||
|
|
||||||
fid = v9fs_fid_lookup_with_uid(dentry, uid, 0);
|
|
||||||
if (IS_ERR(fid))
|
|
||||||
return fid;
|
|
||||||
|
|
||||||
ret = p9_client_walk(fid, 0, NULL, 1);
|
|
||||||
return ret;
|
|
||||||
}
|
|
||||||
|
|
||||||
struct p9_fid *v9fs_writeback_fid(struct dentry *dentry)
|
|
||||||
{
|
|
||||||
int err;
|
|
||||||
struct p9_fid *fid;
|
|
||||||
|
|
||||||
fid = v9fs_fid_clone_with_uid(dentry, GLOBAL_ROOT_UID);
|
|
||||||
if (IS_ERR(fid))
|
|
||||||
goto error_out;
|
|
||||||
/*
|
|
||||||
* writeback fid will only be used to write back the
|
|
||||||
* dirty pages. We always request for the open fid in read-write
|
|
||||||
* mode so that a partial page write which result in page
|
|
||||||
* read can work.
|
|
||||||
*/
|
|
||||||
err = p9_client_open(fid, O_RDWR);
|
|
||||||
if (err < 0) {
|
|
||||||
p9_client_clunk(fid);
|
|
||||||
fid = ERR_PTR(err);
|
|
||||||
goto error_out;
|
|
||||||
}
|
|
||||||
error_out:
|
|
||||||
return fid;
|
|
||||||
}
|
|
@ -1,30 +0,0 @@
|
|||||||
/*
|
|
||||||
* V9FS FID Management
|
|
||||||
*
|
|
||||||
* Copyright (C) 2005 by Eric Van Hensbergen <ericvh@gmail.com>
|
|
||||||
*
|
|
||||||
* This program is free software; you can redistribute it and/or modify
|
|
||||||
* it under the terms of the GNU General Public License version 2
|
|
||||||
* as published by the Free Software Foundation.
|
|
||||||
*
|
|
||||||
* This program is distributed in the hope that it will be useful,
|
|
||||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
||||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
||||||
* GNU General Public License for more details.
|
|
||||||
*
|
|
||||||
* You should have received a copy of the GNU General Public License
|
|
||||||
* along with this program; if not, write to:
|
|
||||||
* Free Software Foundation
|
|
||||||
* 51 Franklin Street, Fifth Floor
|
|
||||||
* Boston, MA 02111-1301 USA
|
|
||||||
*
|
|
||||||
*/
|
|
||||||
#ifndef FS_9P_FID_H
|
|
||||||
#define FS_9P_FID_H
|
|
||||||
#include <linux/list.h>
|
|
||||||
|
|
||||||
struct p9_fid *v9fs_fid_lookup(struct dentry *dentry);
|
|
||||||
struct p9_fid *v9fs_fid_clone(struct dentry *dentry);
|
|
||||||
void v9fs_fid_add(struct dentry *dentry, struct p9_fid *fid);
|
|
||||||
struct p9_fid *v9fs_writeback_fid(struct dentry *dentry);
|
|
||||||
#endif
|
|
@ -1,201 +0,0 @@
|
|||||||
/*
|
|
||||||
* net/9p/9p.c
|
|
||||||
*
|
|
||||||
* 9P entry point
|
|
||||||
*
|
|
||||||
* Copyright (C) 2007 by Latchesar Ionkov <lucho@ionkov.net>
|
|
||||||
* Copyright (C) 2004 by Eric Van Hensbergen <ericvh@gmail.com>
|
|
||||||
* Copyright (C) 2002 by Ron Minnich <rminnich@lanl.gov>
|
|
||||||
*
|
|
||||||
* This program is free software; you can redistribute it and/or modify
|
|
||||||
* it under the terms of the GNU General Public License version 2
|
|
||||||
* as published by the Free Software Foundation.
|
|
||||||
*
|
|
||||||
* This program is distributed in the hope that it will be useful,
|
|
||||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
||||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
||||||
* GNU General Public License for more details.
|
|
||||||
*
|
|
||||||
* You should have received a copy of the GNU General Public License
|
|
||||||
* along with this program; if not, write to:
|
|
||||||
* Free Software Foundation
|
|
||||||
* 51 Franklin Street, Fifth Floor
|
|
||||||
* Boston, MA 02111-1301 USA
|
|
||||||
*
|
|
||||||
*/
|
|
||||||
|
|
||||||
#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
|
|
||||||
|
|
||||||
#include <linux/module.h>
|
|
||||||
#include <linux/errno.h>
|
|
||||||
#include <linux/sched.h>
|
|
||||||
#include <linux/moduleparam.h>
|
|
||||||
#include <net/9p/9p.h>
|
|
||||||
#include <linux/fs.h>
|
|
||||||
#include <linux/parser.h>
|
|
||||||
#include <net/9p/client.h>
|
|
||||||
#include <net/9p/transport.h>
|
|
||||||
#include <linux/list.h>
|
|
||||||
#include <linux/spinlock.h>
|
|
||||||
|
|
||||||
#ifdef CONFIG_NET_9P_DEBUG
|
|
||||||
unsigned int p9_debug_level = 0; /* feature-rific global debug level */
|
|
||||||
EXPORT_SYMBOL(p9_debug_level);
|
|
||||||
module_param_named(debug, p9_debug_level, uint, 0);
|
|
||||||
MODULE_PARM_DESC(debug, "9P debugging level");
|
|
||||||
|
|
||||||
void _p9_debug(enum p9_debug_flags level, const char *func,
|
|
||||||
const char *fmt, ...)
|
|
||||||
{
|
|
||||||
struct va_format vaf;
|
|
||||||
va_list args;
|
|
||||||
|
|
||||||
if ((p9_debug_level & level) != level)
|
|
||||||
return;
|
|
||||||
|
|
||||||
va_start(args, fmt);
|
|
||||||
|
|
||||||
vaf.fmt = fmt;
|
|
||||||
vaf.va = &args;
|
|
||||||
|
|
||||||
if (level == P9_DEBUG_9P)
|
|
||||||
pr_notice("(%8.8d) %pV", task_pid_nr(current), &vaf);
|
|
||||||
else
|
|
||||||
pr_notice("-- %s (%d): %pV", func, task_pid_nr(current), &vaf);
|
|
||||||
|
|
||||||
va_end(args);
|
|
||||||
}
|
|
||||||
EXPORT_SYMBOL(_p9_debug);
|
|
||||||
#endif
|
|
||||||
|
|
||||||
/*
|
|
||||||
* Dynamic Transport Registration Routines
|
|
||||||
*
|
|
||||||
*/
|
|
||||||
|
|
||||||
static DEFINE_SPINLOCK(v9fs_trans_lock);
|
|
||||||
static LIST_HEAD(v9fs_trans_list);
|
|
||||||
|
|
||||||
/**
|
|
||||||
* v9fs_register_trans - register a new transport with 9p
|
|
||||||
* @m: structure describing the transport module and entry points
|
|
||||||
*
|
|
||||||
*/
|
|
||||||
void v9fs_register_trans(struct p9_trans_module *m)
|
|
||||||
{
|
|
||||||
spin_lock(&v9fs_trans_lock);
|
|
||||||
list_add_tail(&m->list, &v9fs_trans_list);
|
|
||||||
spin_unlock(&v9fs_trans_lock);
|
|
||||||
}
|
|
||||||
EXPORT_SYMBOL(v9fs_register_trans);
|
|
||||||
|
|
||||||
/**
|
|
||||||
* v9fs_unregister_trans - unregister a 9p transport
|
|
||||||
* @m: the transport to remove
|
|
||||||
*
|
|
||||||
*/
|
|
||||||
void v9fs_unregister_trans(struct p9_trans_module *m)
|
|
||||||
{
|
|
||||||
spin_lock(&v9fs_trans_lock);
|
|
||||||
list_del_init(&m->list);
|
|
||||||
spin_unlock(&v9fs_trans_lock);
|
|
||||||
}
|
|
||||||
EXPORT_SYMBOL(v9fs_unregister_trans);
|
|
||||||
|
|
||||||
/**
|
|
||||||
* v9fs_get_trans_by_name - get transport with the matching name
|
|
||||||
* @name: string identifying transport
|
|
||||||
*
|
|
||||||
*/
|
|
||||||
struct p9_trans_module *v9fs_get_trans_by_name(char *s)
|
|
||||||
{
|
|
||||||
struct p9_trans_module *t, *found = NULL;
|
|
||||||
|
|
||||||
spin_lock(&v9fs_trans_lock);
|
|
||||||
|
|
||||||
list_for_each_entry(t, &v9fs_trans_list, list)
|
|
||||||
if (strcmp(t->name, s) == 0 &&
|
|
||||||
try_module_get(t->owner)) {
|
|
||||||
found = t;
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
|
|
||||||
spin_unlock(&v9fs_trans_lock);
|
|
||||||
return found;
|
|
||||||
}
|
|
||||||
EXPORT_SYMBOL(v9fs_get_trans_by_name);
|
|
||||||
|
|
||||||
/**
|
|
||||||
* v9fs_get_default_trans - get the default transport
|
|
||||||
*
|
|
||||||
*/
|
|
||||||
|
|
||||||
struct p9_trans_module *v9fs_get_default_trans(void)
|
|
||||||
{
|
|
||||||
struct p9_trans_module *t, *found = NULL;
|
|
||||||
|
|
||||||
spin_lock(&v9fs_trans_lock);
|
|
||||||
|
|
||||||
list_for_each_entry(t, &v9fs_trans_list, list)
|
|
||||||
if (t->def && try_module_get(t->owner)) {
|
|
||||||
found = t;
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (!found)
|
|
||||||
list_for_each_entry(t, &v9fs_trans_list, list)
|
|
||||||
if (try_module_get(t->owner)) {
|
|
||||||
found = t;
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
|
|
||||||
spin_unlock(&v9fs_trans_lock);
|
|
||||||
return found;
|
|
||||||
}
|
|
||||||
EXPORT_SYMBOL(v9fs_get_default_trans);
|
|
||||||
|
|
||||||
/**
|
|
||||||
* v9fs_put_trans - put trans
|
|
||||||
* @m: transport to put
|
|
||||||
*
|
|
||||||
*/
|
|
||||||
void v9fs_put_trans(struct p9_trans_module *m)
|
|
||||||
{
|
|
||||||
if (m)
|
|
||||||
module_put(m->owner);
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* init_p9 - Initialize module
|
|
||||||
*
|
|
||||||
*/
|
|
||||||
static int __init init_p9(void)
|
|
||||||
{
|
|
||||||
int ret = 0;
|
|
||||||
|
|
||||||
p9_error_init();
|
|
||||||
pr_info("Installing 9P2000 support\n");
|
|
||||||
p9_trans_fd_init();
|
|
||||||
|
|
||||||
return ret;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* exit_p9 - shutdown module
|
|
||||||
*
|
|
||||||
*/
|
|
||||||
|
|
||||||
static void __exit exit_p9(void)
|
|
||||||
{
|
|
||||||
pr_info("Unloading 9P2000 support\n");
|
|
||||||
|
|
||||||
p9_trans_fd_exit();
|
|
||||||
}
|
|
||||||
|
|
||||||
module_init(init_p9)
|
|
||||||
module_exit(exit_p9)
|
|
||||||
|
|
||||||
MODULE_AUTHOR("Latchesar Ionkov <lucho@ionkov.net>");
|
|
||||||
MODULE_AUTHOR("Eric Van Hensbergen <ericvh@gmail.com>");
|
|
||||||
MODULE_AUTHOR("Ron Minnich <rminnich@lanl.gov>");
|
|
||||||
MODULE_LICENSE("GPL");
|
|
@ -1,634 +0,0 @@
|
|||||||
/*
|
|
||||||
* net/9p/protocol.c
|
|
||||||
*
|
|
||||||
* 9P Protocol Support Code
|
|
||||||
*
|
|
||||||
* Copyright (C) 2008 by Eric Van Hensbergen <ericvh@gmail.com>
|
|
||||||
*
|
|
||||||
* Base on code from Anthony Liguori <aliguori@us.ibm.com>
|
|
||||||
* Copyright (C) 2008 by IBM, Corp.
|
|
||||||
*
|
|
||||||
* This program is free software; you can redistribute it and/or modify
|
|
||||||
* it under the terms of the GNU General Public License version 2
|
|
||||||
* as published by the Free Software Foundation.
|
|
||||||
*
|
|
||||||
* This program is distributed in the hope that it will be useful,
|
|
||||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
||||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
||||||
* GNU General Public License for more details.
|
|
||||||
*
|
|
||||||
* You should have received a copy of the GNU General Public License
|
|
||||||
* along with this program; if not, write to:
|
|
||||||
* Free Software Foundation
|
|
||||||
* 51 Franklin Street, Fifth Floor
|
|
||||||
* Boston, MA 02111-1301 USA
|
|
||||||
*
|
|
||||||
*/
|
|
||||||
|
|
||||||
#include <linux/module.h>
|
|
||||||
#include <linux/errno.h>
|
|
||||||
#include <linux/kernel.h>
|
|
||||||
#include <linux/uaccess.h>
|
|
||||||
#include <linux/slab.h>
|
|
||||||
#include <linux/sched.h>
|
|
||||||
#include <linux/stddef.h>
|
|
||||||
#include <linux/types.h>
|
|
||||||
#include <linux/uio.h>
|
|
||||||
#include <net/9p/9p.h>
|
|
||||||
#include <net/9p/client.h>
|
|
||||||
#include "protocol.h"
|
|
||||||
|
|
||||||
#include <trace/events/9p.h>
|
|
||||||
|
|
||||||
static int
|
|
||||||
p9pdu_writef(struct p9_fcall *pdu, int proto_version, const char *fmt, ...);
|
|
||||||
|
|
||||||
void p9stat_free(struct p9_wstat *stbuf)
|
|
||||||
{
|
|
||||||
kfree(stbuf->name);
|
|
||||||
stbuf->name = NULL;
|
|
||||||
kfree(stbuf->uid);
|
|
||||||
stbuf->uid = NULL;
|
|
||||||
kfree(stbuf->gid);
|
|
||||||
stbuf->gid = NULL;
|
|
||||||
kfree(stbuf->muid);
|
|
||||||
stbuf->muid = NULL;
|
|
||||||
kfree(stbuf->extension);
|
|
||||||
stbuf->extension = NULL;
|
|
||||||
}
|
|
||||||
EXPORT_SYMBOL(p9stat_free);
|
|
||||||
|
|
||||||
size_t pdu_read(struct p9_fcall *pdu, void *data, size_t size)
|
|
||||||
{
|
|
||||||
size_t len = min(pdu->size - pdu->offset, size);
|
|
||||||
memcpy(data, &pdu->sdata[pdu->offset], len);
|
|
||||||
pdu->offset += len;
|
|
||||||
return size - len;
|
|
||||||
}
|
|
||||||
|
|
||||||
static size_t pdu_write(struct p9_fcall *pdu, const void *data, size_t size)
|
|
||||||
{
|
|
||||||
size_t len = min(pdu->capacity - pdu->size, size);
|
|
||||||
memcpy(&pdu->sdata[pdu->size], data, len);
|
|
||||||
pdu->size += len;
|
|
||||||
return size - len;
|
|
||||||
}
|
|
||||||
|
|
||||||
static size_t
|
|
||||||
pdu_write_u(struct p9_fcall *pdu, struct iov_iter *from, size_t size)
|
|
||||||
{
|
|
||||||
size_t len = min(pdu->capacity - pdu->size, size);
|
|
||||||
struct iov_iter i = *from;
|
|
||||||
if (copy_from_iter(&pdu->sdata[pdu->size], len, &i) != len)
|
|
||||||
len = 0;
|
|
||||||
|
|
||||||
pdu->size += len;
|
|
||||||
return size - len;
|
|
||||||
}
|
|
||||||
|
|
||||||
/*
|
|
||||||
b - int8_t
|
|
||||||
w - int16_t
|
|
||||||
d - int32_t
|
|
||||||
q - int64_t
|
|
||||||
s - string
|
|
||||||
u - numeric uid
|
|
||||||
g - numeric gid
|
|
||||||
S - stat
|
|
||||||
Q - qid
|
|
||||||
D - data blob (int32_t size followed by void *, results are not freed)
|
|
||||||
T - array of strings (int16_t count, followed by strings)
|
|
||||||
R - array of qids (int16_t count, followed by qids)
|
|
||||||
A - stat for 9p2000.L (p9_stat_dotl)
|
|
||||||
? - if optional = 1, continue parsing
|
|
||||||
*/
|
|
||||||
|
|
||||||
static int
|
|
||||||
p9pdu_vreadf(struct p9_fcall *pdu, int proto_version, const char *fmt,
|
|
||||||
va_list ap)
|
|
||||||
{
|
|
||||||
const char *ptr;
|
|
||||||
int errcode = 0;
|
|
||||||
|
|
||||||
for (ptr = fmt; *ptr; ptr++) {
|
|
||||||
switch (*ptr) {
|
|
||||||
case 'b':{
|
|
||||||
int8_t *val = va_arg(ap, int8_t *);
|
|
||||||
if (pdu_read(pdu, val, sizeof(*val))) {
|
|
||||||
errcode = -EFAULT;
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
break;
|
|
||||||
case 'w':{
|
|
||||||
int16_t *val = va_arg(ap, int16_t *);
|
|
||||||
__le16 le_val;
|
|
||||||
if (pdu_read(pdu, &le_val, sizeof(le_val))) {
|
|
||||||
errcode = -EFAULT;
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
*val = le16_to_cpu(le_val);
|
|
||||||
}
|
|
||||||
break;
|
|
||||||
case 'd':{
|
|
||||||
int32_t *val = va_arg(ap, int32_t *);
|
|
||||||
__le32 le_val;
|
|
||||||
if (pdu_read(pdu, &le_val, sizeof(le_val))) {
|
|
||||||
errcode = -EFAULT;
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
*val = le32_to_cpu(le_val);
|
|
||||||
}
|
|
||||||
break;
|
|
||||||
case 'q':{
|
|
||||||
int64_t *val = va_arg(ap, int64_t *);
|
|
||||||
__le64 le_val;
|
|
||||||
if (pdu_read(pdu, &le_val, sizeof(le_val))) {
|
|
||||||
errcode = -EFAULT;
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
*val = le64_to_cpu(le_val);
|
|
||||||
}
|
|
||||||
break;
|
|
||||||
case 's':{
|
|
||||||
char **sptr = va_arg(ap, char **);
|
|
||||||
uint16_t len;
|
|
||||||
|
|
||||||
errcode = p9pdu_readf(pdu, proto_version,
|
|
||||||
"w", &len);
|
|
||||||
if (errcode)
|
|
||||||
break;
|
|
||||||
|
|
||||||
*sptr = kmalloc(len + 1, GFP_NOFS);
|
|
||||||
if (*sptr == NULL) {
|
|
||||||
errcode = -EFAULT;
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
if (pdu_read(pdu, *sptr, len)) {
|
|
||||||
errcode = -EFAULT;
|
|
||||||
kfree(*sptr);
|
|
||||||
*sptr = NULL;
|
|
||||||
} else
|
|
||||||
(*sptr)[len] = 0;
|
|
||||||
}
|
|
||||||
break;
|
|
||||||
case 'u': {
|
|
||||||
kuid_t *uid = va_arg(ap, kuid_t *);
|
|
||||||
__le32 le_val;
|
|
||||||
if (pdu_read(pdu, &le_val, sizeof(le_val))) {
|
|
||||||
errcode = -EFAULT;
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
*uid = make_kuid(&init_user_ns,
|
|
||||||
le32_to_cpu(le_val));
|
|
||||||
} break;
|
|
||||||
case 'g': {
|
|
||||||
kgid_t *gid = va_arg(ap, kgid_t *);
|
|
||||||
__le32 le_val;
|
|
||||||
if (pdu_read(pdu, &le_val, sizeof(le_val))) {
|
|
||||||
errcode = -EFAULT;
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
*gid = make_kgid(&init_user_ns,
|
|
||||||
le32_to_cpu(le_val));
|
|
||||||
} break;
|
|
||||||
case 'Q':{
|
|
||||||
struct p9_qid *qid =
|
|
||||||
va_arg(ap, struct p9_qid *);
|
|
||||||
|
|
||||||
errcode = p9pdu_readf(pdu, proto_version, "bdq",
|
|
||||||
&qid->type, &qid->version,
|
|
||||||
&qid->path);
|
|
||||||
}
|
|
||||||
break;
|
|
||||||
case 'S':{
|
|
||||||
struct p9_wstat *stbuf =
|
|
||||||
va_arg(ap, struct p9_wstat *);
|
|
||||||
|
|
||||||
memset(stbuf, 0, sizeof(struct p9_wstat));
|
|
||||||
stbuf->n_uid = stbuf->n_muid = INVALID_UID;
|
|
||||||
stbuf->n_gid = INVALID_GID;
|
|
||||||
|
|
||||||
errcode =
|
|
||||||
p9pdu_readf(pdu, proto_version,
|
|
||||||
"wwdQdddqssss?sugu",
|
|
||||||
&stbuf->size, &stbuf->type,
|
|
||||||
&stbuf->dev, &stbuf->qid,
|
|
||||||
&stbuf->mode, &stbuf->atime,
|
|
||||||
&stbuf->mtime, &stbuf->length,
|
|
||||||
&stbuf->name, &stbuf->uid,
|
|
||||||
&stbuf->gid, &stbuf->muid,
|
|
||||||
&stbuf->extension,
|
|
||||||
&stbuf->n_uid, &stbuf->n_gid,
|
|
||||||
&stbuf->n_muid);
|
|
||||||
if (errcode)
|
|
||||||
p9stat_free(stbuf);
|
|
||||||
}
|
|
||||||
break;
|
|
||||||
case 'D':{
|
|
||||||
uint32_t *count = va_arg(ap, uint32_t *);
|
|
||||||
void **data = va_arg(ap, void **);
|
|
||||||
|
|
||||||
errcode =
|
|
||||||
p9pdu_readf(pdu, proto_version, "d", count);
|
|
||||||
if (!errcode) {
|
|
||||||
*count =
|
|
||||||
min_t(uint32_t, *count,
|
|
||||||
pdu->size - pdu->offset);
|
|
||||||
*data = &pdu->sdata[pdu->offset];
|
|
||||||
}
|
|
||||||
}
|
|
||||||
break;
|
|
||||||
case 'T':{
|
|
||||||
uint16_t *nwname = va_arg(ap, uint16_t *);
|
|
||||||
char ***wnames = va_arg(ap, char ***);
|
|
||||||
|
|
||||||
errcode = p9pdu_readf(pdu, proto_version,
|
|
||||||
"w", nwname);
|
|
||||||
if (!errcode) {
|
|
||||||
*wnames =
|
|
||||||
kmalloc(sizeof(char *) * *nwname,
|
|
||||||
GFP_NOFS);
|
|
||||||
if (!*wnames)
|
|
||||||
errcode = -ENOMEM;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (!errcode) {
|
|
||||||
int i;
|
|
||||||
|
|
||||||
for (i = 0; i < *nwname; i++) {
|
|
||||||
errcode =
|
|
||||||
p9pdu_readf(pdu,
|
|
||||||
proto_version,
|
|
||||||
"s",
|
|
||||||
&(*wnames)[i]);
|
|
||||||
if (errcode)
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if (errcode) {
|
|
||||||
if (*wnames) {
|
|
||||||
int i;
|
|
||||||
|
|
||||||
for (i = 0; i < *nwname; i++)
|
|
||||||
kfree((*wnames)[i]);
|
|
||||||
}
|
|
||||||
kfree(*wnames);
|
|
||||||
*wnames = NULL;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
break;
|
|
||||||
case 'R':{
|
|
||||||
uint16_t *nwqid = va_arg(ap, uint16_t *);
|
|
||||||
struct p9_qid **wqids =
|
|
||||||
va_arg(ap, struct p9_qid **);
|
|
||||||
|
|
||||||
*wqids = NULL;
|
|
||||||
|
|
||||||
errcode =
|
|
||||||
p9pdu_readf(pdu, proto_version, "w", nwqid);
|
|
||||||
if (!errcode) {
|
|
||||||
*wqids =
|
|
||||||
kmalloc(*nwqid *
|
|
||||||
sizeof(struct p9_qid),
|
|
||||||
GFP_NOFS);
|
|
||||||
if (*wqids == NULL)
|
|
||||||
errcode = -ENOMEM;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (!errcode) {
|
|
||||||
int i;
|
|
||||||
|
|
||||||
for (i = 0; i < *nwqid; i++) {
|
|
||||||
errcode =
|
|
||||||
p9pdu_readf(pdu,
|
|
||||||
proto_version,
|
|
||||||
"Q",
|
|
||||||
&(*wqids)[i]);
|
|
||||||
if (errcode)
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if (errcode) {
|
|
||||||
kfree(*wqids);
|
|
||||||
*wqids = NULL;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
break;
|
|
||||||
case 'A': {
|
|
||||||
struct p9_stat_dotl *stbuf =
|
|
||||||
va_arg(ap, struct p9_stat_dotl *);
|
|
||||||
|
|
||||||
memset(stbuf, 0, sizeof(struct p9_stat_dotl));
|
|
||||||
errcode =
|
|
||||||
p9pdu_readf(pdu, proto_version,
|
|
||||||
"qQdugqqqqqqqqqqqqqqq",
|
|
||||||
&stbuf->st_result_mask,
|
|
||||||
&stbuf->qid,
|
|
||||||
&stbuf->st_mode,
|
|
||||||
&stbuf->st_uid, &stbuf->st_gid,
|
|
||||||
&stbuf->st_nlink,
|
|
||||||
&stbuf->st_rdev, &stbuf->st_size,
|
|
||||||
&stbuf->st_blksize, &stbuf->st_blocks,
|
|
||||||
&stbuf->st_atime_sec,
|
|
||||||
&stbuf->st_atime_nsec,
|
|
||||||
&stbuf->st_mtime_sec,
|
|
||||||
&stbuf->st_mtime_nsec,
|
|
||||||
&stbuf->st_ctime_sec,
|
|
||||||
&stbuf->st_ctime_nsec,
|
|
||||||
&stbuf->st_btime_sec,
|
|
||||||
&stbuf->st_btime_nsec,
|
|
||||||
&stbuf->st_gen,
|
|
||||||
&stbuf->st_data_version);
|
|
||||||
}
|
|
||||||
break;
|
|
||||||
case '?':
|
|
||||||
if ((proto_version != p9_proto_2000u) &&
|
|
||||||
(proto_version != p9_proto_2000L))
|
|
||||||
return 0;
|
|
||||||
break;
|
|
||||||
default:
|
|
||||||
BUG();
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (errcode)
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
|
|
||||||
return errcode;
|
|
||||||
}
|
|
||||||
|
|
||||||
int
|
|
||||||
p9pdu_vwritef(struct p9_fcall *pdu, int proto_version, const char *fmt,
|
|
||||||
va_list ap)
|
|
||||||
{
|
|
||||||
const char *ptr;
|
|
||||||
int errcode = 0;
|
|
||||||
|
|
||||||
for (ptr = fmt; *ptr; ptr++) {
|
|
||||||
switch (*ptr) {
|
|
||||||
case 'b':{
|
|
||||||
int8_t val = va_arg(ap, int);
|
|
||||||
if (pdu_write(pdu, &val, sizeof(val)))
|
|
||||||
errcode = -EFAULT;
|
|
||||||
}
|
|
||||||
break;
|
|
||||||
case 'w':{
|
|
||||||
__le16 val = cpu_to_le16(va_arg(ap, int));
|
|
||||||
if (pdu_write(pdu, &val, sizeof(val)))
|
|
||||||
errcode = -EFAULT;
|
|
||||||
}
|
|
||||||
break;
|
|
||||||
case 'd':{
|
|
||||||
__le32 val = cpu_to_le32(va_arg(ap, int32_t));
|
|
||||||
if (pdu_write(pdu, &val, sizeof(val)))
|
|
||||||
errcode = -EFAULT;
|
|
||||||
}
|
|
||||||
break;
|
|
||||||
case 'q':{
|
|
||||||
__le64 val = cpu_to_le64(va_arg(ap, int64_t));
|
|
||||||
if (pdu_write(pdu, &val, sizeof(val)))
|
|
||||||
errcode = -EFAULT;
|
|
||||||
}
|
|
||||||
break;
|
|
||||||
case 's':{
|
|
||||||
const char *sptr = va_arg(ap, const char *);
|
|
||||||
uint16_t len = 0;
|
|
||||||
if (sptr)
|
|
||||||
len = min_t(size_t, strlen(sptr),
|
|
||||||
USHRT_MAX);
|
|
||||||
|
|
||||||
errcode = p9pdu_writef(pdu, proto_version,
|
|
||||||
"w", len);
|
|
||||||
if (!errcode && pdu_write(pdu, sptr, len))
|
|
||||||
errcode = -EFAULT;
|
|
||||||
}
|
|
||||||
break;
|
|
||||||
case 'u': {
|
|
||||||
kuid_t uid = va_arg(ap, kuid_t);
|
|
||||||
__le32 val = cpu_to_le32(
|
|
||||||
from_kuid(&init_user_ns, uid));
|
|
||||||
if (pdu_write(pdu, &val, sizeof(val)))
|
|
||||||
errcode = -EFAULT;
|
|
||||||
} break;
|
|
||||||
case 'g': {
|
|
||||||
kgid_t gid = va_arg(ap, kgid_t);
|
|
||||||
__le32 val = cpu_to_le32(
|
|
||||||
from_kgid(&init_user_ns, gid));
|
|
||||||
if (pdu_write(pdu, &val, sizeof(val)))
|
|
||||||
errcode = -EFAULT;
|
|
||||||
} break;
|
|
||||||
case 'Q':{
|
|
||||||
const struct p9_qid *qid =
|
|
||||||
va_arg(ap, const struct p9_qid *);
|
|
||||||
errcode =
|
|
||||||
p9pdu_writef(pdu, proto_version, "bdq",
|
|
||||||
qid->type, qid->version,
|
|
||||||
qid->path);
|
|
||||||
} break;
|
|
||||||
case 'S':{
|
|
||||||
const struct p9_wstat *stbuf =
|
|
||||||
va_arg(ap, const struct p9_wstat *);
|
|
||||||
errcode =
|
|
||||||
p9pdu_writef(pdu, proto_version,
|
|
||||||
"wwdQdddqssss?sugu",
|
|
||||||
stbuf->size, stbuf->type,
|
|
||||||
stbuf->dev, &stbuf->qid,
|
|
||||||
stbuf->mode, stbuf->atime,
|
|
||||||
stbuf->mtime, stbuf->length,
|
|
||||||
stbuf->name, stbuf->uid,
|
|
||||||
stbuf->gid, stbuf->muid,
|
|
||||||
stbuf->extension, stbuf->n_uid,
|
|
||||||
stbuf->n_gid, stbuf->n_muid);
|
|
||||||
} break;
|
|
||||||
case 'V':{
|
|
||||||
uint32_t count = va_arg(ap, uint32_t);
|
|
||||||
struct iov_iter *from =
|
|
||||||
va_arg(ap, struct iov_iter *);
|
|
||||||
errcode = p9pdu_writef(pdu, proto_version, "d",
|
|
||||||
count);
|
|
||||||
if (!errcode && pdu_write_u(pdu, from, count))
|
|
||||||
errcode = -EFAULT;
|
|
||||||
}
|
|
||||||
break;
|
|
||||||
case 'T':{
|
|
||||||
uint16_t nwname = va_arg(ap, int);
|
|
||||||
const char **wnames = va_arg(ap, const char **);
|
|
||||||
|
|
||||||
errcode = p9pdu_writef(pdu, proto_version, "w",
|
|
||||||
nwname);
|
|
||||||
if (!errcode) {
|
|
||||||
int i;
|
|
||||||
|
|
||||||
for (i = 0; i < nwname; i++) {
|
|
||||||
errcode =
|
|
||||||
p9pdu_writef(pdu,
|
|
||||||
proto_version,
|
|
||||||
"s",
|
|
||||||
wnames[i]);
|
|
||||||
if (errcode)
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
break;
|
|
||||||
case 'R':{
|
|
||||||
uint16_t nwqid = va_arg(ap, int);
|
|
||||||
struct p9_qid *wqids =
|
|
||||||
va_arg(ap, struct p9_qid *);
|
|
||||||
|
|
||||||
errcode = p9pdu_writef(pdu, proto_version, "w",
|
|
||||||
nwqid);
|
|
||||||
if (!errcode) {
|
|
||||||
int i;
|
|
||||||
|
|
||||||
for (i = 0; i < nwqid; i++) {
|
|
||||||
errcode =
|
|
||||||
p9pdu_writef(pdu,
|
|
||||||
proto_version,
|
|
||||||
"Q",
|
|
||||||
&wqids[i]);
|
|
||||||
if (errcode)
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
break;
|
|
||||||
case 'I':{
|
|
||||||
struct p9_iattr_dotl *p9attr = va_arg(ap,
|
|
||||||
struct p9_iattr_dotl *);
|
|
||||||
|
|
||||||
errcode = p9pdu_writef(pdu, proto_version,
|
|
||||||
"ddugqqqqq",
|
|
||||||
p9attr->valid,
|
|
||||||
p9attr->mode,
|
|
||||||
p9attr->uid,
|
|
||||||
p9attr->gid,
|
|
||||||
p9attr->size,
|
|
||||||
p9attr->atime_sec,
|
|
||||||
p9attr->atime_nsec,
|
|
||||||
p9attr->mtime_sec,
|
|
||||||
p9attr->mtime_nsec);
|
|
||||||
}
|
|
||||||
break;
|
|
||||||
case '?':
|
|
||||||
if ((proto_version != p9_proto_2000u) &&
|
|
||||||
(proto_version != p9_proto_2000L))
|
|
||||||
return 0;
|
|
||||||
break;
|
|
||||||
default:
|
|
||||||
BUG();
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (errcode)
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
|
|
||||||
return errcode;
|
|
||||||
}
|
|
||||||
|
|
||||||
int p9pdu_readf(struct p9_fcall *pdu, int proto_version, const char *fmt, ...)
|
|
||||||
{
|
|
||||||
va_list ap;
|
|
||||||
int ret;
|
|
||||||
|
|
||||||
va_start(ap, fmt);
|
|
||||||
ret = p9pdu_vreadf(pdu, proto_version, fmt, ap);
|
|
||||||
va_end(ap);
|
|
||||||
|
|
||||||
return ret;
|
|
||||||
}
|
|
||||||
|
|
||||||
static int
|
|
||||||
p9pdu_writef(struct p9_fcall *pdu, int proto_version, const char *fmt, ...)
|
|
||||||
{
|
|
||||||
va_list ap;
|
|
||||||
int ret;
|
|
||||||
|
|
||||||
va_start(ap, fmt);
|
|
||||||
ret = p9pdu_vwritef(pdu, proto_version, fmt, ap);
|
|
||||||
va_end(ap);
|
|
||||||
|
|
||||||
return ret;
|
|
||||||
}
|
|
||||||
|
|
||||||
int p9stat_read(struct p9_client *clnt, char *buf, int len, struct p9_wstat *st)
|
|
||||||
{
|
|
||||||
struct p9_fcall fake_pdu;
|
|
||||||
int ret;
|
|
||||||
|
|
||||||
fake_pdu.size = len;
|
|
||||||
fake_pdu.capacity = len;
|
|
||||||
fake_pdu.sdata = buf;
|
|
||||||
fake_pdu.offset = 0;
|
|
||||||
|
|
||||||
ret = p9pdu_readf(&fake_pdu, clnt->proto_version, "S", st);
|
|
||||||
if (ret) {
|
|
||||||
p9_debug(P9_DEBUG_9P, "<<< p9stat_read failed: %d\n", ret);
|
|
||||||
trace_9p_protocol_dump(clnt, &fake_pdu);
|
|
||||||
return ret;
|
|
||||||
}
|
|
||||||
|
|
||||||
return fake_pdu.offset;
|
|
||||||
}
|
|
||||||
EXPORT_SYMBOL(p9stat_read);
|
|
||||||
|
|
||||||
int p9pdu_prepare(struct p9_fcall *pdu, int16_t tag, int8_t type)
|
|
||||||
{
|
|
||||||
pdu->id = type;
|
|
||||||
return p9pdu_writef(pdu, 0, "dbw", 0, type, tag);
|
|
||||||
}
|
|
||||||
|
|
||||||
int p9pdu_finalize(struct p9_client *clnt, struct p9_fcall *pdu)
|
|
||||||
{
|
|
||||||
int size = pdu->size;
|
|
||||||
int err;
|
|
||||||
|
|
||||||
pdu->size = 0;
|
|
||||||
err = p9pdu_writef(pdu, 0, "d", size);
|
|
||||||
pdu->size = size;
|
|
||||||
|
|
||||||
trace_9p_protocol_dump(clnt, pdu);
|
|
||||||
p9_debug(P9_DEBUG_9P, ">>> size=%d type: %d tag: %d\n",
|
|
||||||
pdu->size, pdu->id, pdu->tag);
|
|
||||||
|
|
||||||
return err;
|
|
||||||
}
|
|
||||||
|
|
||||||
void p9pdu_reset(struct p9_fcall *pdu)
|
|
||||||
{
|
|
||||||
pdu->offset = 0;
|
|
||||||
pdu->size = 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
int p9dirent_read(struct p9_client *clnt, char *buf, int len,
|
|
||||||
struct p9_dirent *dirent)
|
|
||||||
{
|
|
||||||
struct p9_fcall fake_pdu;
|
|
||||||
int ret;
|
|
||||||
char *nameptr;
|
|
||||||
|
|
||||||
fake_pdu.size = len;
|
|
||||||
fake_pdu.capacity = len;
|
|
||||||
fake_pdu.sdata = buf;
|
|
||||||
fake_pdu.offset = 0;
|
|
||||||
|
|
||||||
ret = p9pdu_readf(&fake_pdu, clnt->proto_version, "Qqbs", &dirent->qid,
|
|
||||||
&dirent->d_off, &dirent->d_type, &nameptr);
|
|
||||||
if (ret) {
|
|
||||||
p9_debug(P9_DEBUG_9P, "<<< p9dirent_read failed: %d\n", ret);
|
|
||||||
trace_9p_protocol_dump(clnt, &fake_pdu);
|
|
||||||
goto out;
|
|
||||||
}
|
|
||||||
|
|
||||||
strcpy(dirent->d_name, nameptr);
|
|
||||||
kfree(nameptr);
|
|
||||||
|
|
||||||
out:
|
|
||||||
return fake_pdu.offset;
|
|
||||||
}
|
|
||||||
EXPORT_SYMBOL(p9dirent_read);
|
|
@ -1,34 +0,0 @@
|
|||||||
/*
|
|
||||||
* net/9p/protocol.h
|
|
||||||
*
|
|
||||||
* 9P Protocol Support Code
|
|
||||||
*
|
|
||||||
* Copyright (C) 2008 by Eric Van Hensbergen <ericvh@gmail.com>
|
|
||||||
*
|
|
||||||
* Base on code from Anthony Liguori <aliguori@us.ibm.com>
|
|
||||||
* Copyright (C) 2008 by IBM, Corp.
|
|
||||||
*
|
|
||||||
* This program is free software; you can redistribute it and/or modify
|
|
||||||
* it under the terms of the GNU General Public License version 2
|
|
||||||
* as published by the Free Software Foundation.
|
|
||||||
*
|
|
||||||
* This program is distributed in the hope that it will be useful,
|
|
||||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
||||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
||||||
* GNU General Public License for more details.
|
|
||||||
*
|
|
||||||
* You should have received a copy of the GNU General Public License
|
|
||||||
* along with this program; if not, write to:
|
|
||||||
* Free Software Foundation
|
|
||||||
* 51 Franklin Street, Fifth Floor
|
|
||||||
* Boston, MA 02111-1301 USA
|
|
||||||
*
|
|
||||||
*/
|
|
||||||
|
|
||||||
int p9pdu_vwritef(struct p9_fcall *pdu, int proto_version, const char *fmt,
|
|
||||||
va_list ap);
|
|
||||||
int p9pdu_readf(struct p9_fcall *pdu, int proto_version, const char *fmt, ...);
|
|
||||||
int p9pdu_prepare(struct p9_fcall *pdu, int16_t tag, int8_t type);
|
|
||||||
int p9pdu_finalize(struct p9_client *clnt, struct p9_fcall *pdu);
|
|
||||||
void p9pdu_reset(struct p9_fcall *pdu);
|
|
||||||
size_t pdu_read(struct p9_fcall *pdu, void *data, size_t size);
|
|
@ -1,29 +0,0 @@
|
|||||||
/*
|
|
||||||
* Copyright IBM Corporation, 2010
|
|
||||||
* Author Venkateswararao Jujjuri <jvrao@linux.vnet.ibm.com>
|
|
||||||
*
|
|
||||||
* This program is free software; you can redistribute it and/or modify it
|
|
||||||
* under the terms of version 2.1 of the GNU Lesser General Public License
|
|
||||||
* as published by the Free Software Foundation.
|
|
||||||
*
|
|
||||||
* This program is distributed in the hope that it would be useful, but
|
|
||||||
* WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
||||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
|
|
||||||
*
|
|
||||||
*/
|
|
||||||
|
|
||||||
#include <linux/mm.h>
|
|
||||||
#include <linux/module.h>
|
|
||||||
|
|
||||||
/**
|
|
||||||
* p9_release_req_pages - Release pages after the transaction.
|
|
||||||
*/
|
|
||||||
void p9_release_pages(struct page **pages, int nr_pages)
|
|
||||||
{
|
|
||||||
int i;
|
|
||||||
|
|
||||||
for (i = 0; i < nr_pages; i++)
|
|
||||||
if (pages[i])
|
|
||||||
put_page(pages[i]);
|
|
||||||
}
|
|
||||||
EXPORT_SYMBOL(p9_release_pages);
|
|
@ -1,15 +0,0 @@
|
|||||||
/*
|
|
||||||
* Copyright IBM Corporation, 2010
|
|
||||||
* Author Venkateswararao Jujjuri <jvrao@linux.vnet.ibm.com>
|
|
||||||
*
|
|
||||||
* This program is free software; you can redistribute it and/or modify it
|
|
||||||
* under the terms of version 2.1 of the GNU Lesser General Public License
|
|
||||||
* as published by the Free Software Foundation.
|
|
||||||
*
|
|
||||||
* This program is distributed in the hope that it would be useful, but
|
|
||||||
* WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
||||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
|
|
||||||
*
|
|
||||||
*/
|
|
||||||
|
|
||||||
void p9_release_pages(struct page **, int);
|
|
File diff suppressed because it is too large
Load Diff
@ -1,784 +0,0 @@
|
|||||||
/*
|
|
||||||
* linux/fs/9p/trans_rdma.c
|
|
||||||
*
|
|
||||||
* RDMA transport layer based on the trans_fd.c implementation.
|
|
||||||
*
|
|
||||||
* Copyright (C) 2008 by Tom Tucker <tom@opengridcomputing.com>
|
|
||||||
* Copyright (C) 2006 by Russ Cox <rsc@swtch.com>
|
|
||||||
* Copyright (C) 2004-2005 by Latchesar Ionkov <lucho@ionkov.net>
|
|
||||||
* Copyright (C) 2004-2008 by Eric Van Hensbergen <ericvh@gmail.com>
|
|
||||||
* Copyright (C) 1997-2002 by Ron Minnich <rminnich@sarnoff.com>
|
|
||||||
*
|
|
||||||
* This program is free software; you can redistribute it and/or modify
|
|
||||||
* it under the terms of the GNU General Public License version 2
|
|
||||||
* as published by the Free Software Foundation.
|
|
||||||
*
|
|
||||||
* This program is distributed in the hope that it will be useful,
|
|
||||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
||||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
||||||
* GNU General Public License for more details.
|
|
||||||
*
|
|
||||||
* You should have received a copy of the GNU General Public License
|
|
||||||
* along with this program; if not, write to:
|
|
||||||
* Free Software Foundation
|
|
||||||
* 51 Franklin Street, Fifth Floor
|
|
||||||
* Boston, MA 02111-1301 USA
|
|
||||||
*
|
|
||||||
*/
|
|
||||||
|
|
||||||
#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
|
|
||||||
|
|
||||||
#include <linux/in.h>
|
|
||||||
#include <linux/module.h>
|
|
||||||
#include <linux/net.h>
|
|
||||||
#include <linux/ipv6.h>
|
|
||||||
#include <linux/kthread.h>
|
|
||||||
#include <linux/errno.h>
|
|
||||||
#include <linux/kernel.h>
|
|
||||||
#include <linux/un.h>
|
|
||||||
#include <linux/uaccess.h>
|
|
||||||
#include <linux/inet.h>
|
|
||||||
#include <linux/idr.h>
|
|
||||||
#include <linux/file.h>
|
|
||||||
#include <linux/parser.h>
|
|
||||||
#include <linux/semaphore.h>
|
|
||||||
#include <linux/slab.h>
|
|
||||||
#include <net/9p/9p.h>
|
|
||||||
#include <net/9p/client.h>
|
|
||||||
#include <net/9p/transport.h>
|
|
||||||
#include <rdma/ib_verbs.h>
|
|
||||||
#include <rdma/rdma_cm.h>
|
|
||||||
|
|
||||||
#define P9_PORT 5640
|
|
||||||
#define P9_RDMA_SQ_DEPTH 32
|
|
||||||
#define P9_RDMA_RQ_DEPTH 32
|
|
||||||
#define P9_RDMA_SEND_SGE 4
|
|
||||||
#define P9_RDMA_RECV_SGE 4
|
|
||||||
#define P9_RDMA_IRD 0
|
|
||||||
#define P9_RDMA_ORD 0
|
|
||||||
#define P9_RDMA_TIMEOUT 30000 /* 30 seconds */
|
|
||||||
#define P9_RDMA_MAXSIZE (1024*1024) /* 1MB */
|
|
||||||
|
|
||||||
/**
|
|
||||||
* struct p9_trans_rdma - RDMA transport instance
|
|
||||||
*
|
|
||||||
* @state: tracks the transport state machine for connection setup and tear down
|
|
||||||
* @cm_id: The RDMA CM ID
|
|
||||||
* @pd: Protection Domain pointer
|
|
||||||
* @qp: Queue Pair pointer
|
|
||||||
* @cq: Completion Queue pointer
|
|
||||||
* @dm_mr: DMA Memory Region pointer
|
|
||||||
* @lkey: The local access only memory region key
|
|
||||||
* @timeout: Number of uSecs to wait for connection management events
|
|
||||||
* @sq_depth: The depth of the Send Queue
|
|
||||||
* @sq_sem: Semaphore for the SQ
|
|
||||||
* @rq_depth: The depth of the Receive Queue.
|
|
||||||
* @rq_sem: Semaphore for the RQ
|
|
||||||
* @excess_rc : Amount of posted Receive Contexts without a pending request.
|
|
||||||
* See rdma_request()
|
|
||||||
* @addr: The remote peer's address
|
|
||||||
* @req_lock: Protects the active request list
|
|
||||||
* @cm_done: Completion event for connection management tracking
|
|
||||||
*/
|
|
||||||
struct p9_trans_rdma {
|
|
||||||
enum {
|
|
||||||
P9_RDMA_INIT,
|
|
||||||
P9_RDMA_ADDR_RESOLVED,
|
|
||||||
P9_RDMA_ROUTE_RESOLVED,
|
|
||||||
P9_RDMA_CONNECTED,
|
|
||||||
P9_RDMA_FLUSHING,
|
|
||||||
P9_RDMA_CLOSING,
|
|
||||||
P9_RDMA_CLOSED,
|
|
||||||
} state;
|
|
||||||
struct rdma_cm_id *cm_id;
|
|
||||||
struct ib_pd *pd;
|
|
||||||
struct ib_qp *qp;
|
|
||||||
struct ib_cq *cq;
|
|
||||||
long timeout;
|
|
||||||
int sq_depth;
|
|
||||||
struct semaphore sq_sem;
|
|
||||||
int rq_depth;
|
|
||||||
struct semaphore rq_sem;
|
|
||||||
atomic_t excess_rc;
|
|
||||||
struct sockaddr_in addr;
|
|
||||||
spinlock_t req_lock;
|
|
||||||
|
|
||||||
struct completion cm_done;
|
|
||||||
};
|
|
||||||
|
|
||||||
/**
|
|
||||||
* p9_rdma_context - Keeps track of in-process WR
|
|
||||||
*
|
|
||||||
* @wc_op: The original WR op for when the CQE completes in error.
|
|
||||||
* @busa: Bus address to unmap when the WR completes
|
|
||||||
* @req: Keeps track of requests (send)
|
|
||||||
* @rc: Keepts track of replies (receive)
|
|
||||||
*/
|
|
||||||
struct p9_rdma_req;
|
|
||||||
struct p9_rdma_context {
|
|
||||||
enum ib_wc_opcode wc_op;
|
|
||||||
dma_addr_t busa;
|
|
||||||
union {
|
|
||||||
struct p9_req_t *req;
|
|
||||||
struct p9_fcall *rc;
|
|
||||||
};
|
|
||||||
};
|
|
||||||
|
|
||||||
/**
|
|
||||||
* p9_rdma_opts - Collection of mount options
|
|
||||||
* @port: port of connection
|
|
||||||
* @sq_depth: The requested depth of the SQ. This really doesn't need
|
|
||||||
* to be any deeper than the number of threads used in the client
|
|
||||||
* @rq_depth: The depth of the RQ. Should be greater than or equal to SQ depth
|
|
||||||
* @timeout: Time to wait in msecs for CM events
|
|
||||||
*/
|
|
||||||
struct p9_rdma_opts {
|
|
||||||
short port;
|
|
||||||
int sq_depth;
|
|
||||||
int rq_depth;
|
|
||||||
long timeout;
|
|
||||||
int privport;
|
|
||||||
};
|
|
||||||
|
|
||||||
/*
|
|
||||||
* Option Parsing (code inspired by NFS code)
|
|
||||||
*/
|
|
||||||
enum {
|
|
||||||
/* Options that take integer arguments */
|
|
||||||
Opt_port, Opt_rq_depth, Opt_sq_depth, Opt_timeout,
|
|
||||||
/* Options that take no argument */
|
|
||||||
Opt_privport,
|
|
||||||
Opt_err,
|
|
||||||
};
|
|
||||||
|
|
||||||
static match_table_t tokens = {
|
|
||||||
{Opt_port, "port=%u"},
|
|
||||||
{Opt_sq_depth, "sq=%u"},
|
|
||||||
{Opt_rq_depth, "rq=%u"},
|
|
||||||
{Opt_timeout, "timeout=%u"},
|
|
||||||
{Opt_privport, "privport"},
|
|
||||||
{Opt_err, NULL},
|
|
||||||
};
|
|
||||||
|
|
||||||
/**
|
|
||||||
* parse_opts - parse mount options into rdma options structure
|
|
||||||
* @params: options string passed from mount
|
|
||||||
* @opts: rdma transport-specific structure to parse options into
|
|
||||||
*
|
|
||||||
* Returns 0 upon success, -ERRNO upon failure
|
|
||||||
*/
|
|
||||||
static int parse_opts(char *params, struct p9_rdma_opts *opts)
|
|
||||||
{
|
|
||||||
char *p;
|
|
||||||
substring_t args[MAX_OPT_ARGS];
|
|
||||||
int option;
|
|
||||||
char *options, *tmp_options;
|
|
||||||
|
|
||||||
opts->port = P9_PORT;
|
|
||||||
opts->sq_depth = P9_RDMA_SQ_DEPTH;
|
|
||||||
opts->rq_depth = P9_RDMA_RQ_DEPTH;
|
|
||||||
opts->timeout = P9_RDMA_TIMEOUT;
|
|
||||||
opts->privport = 0;
|
|
||||||
|
|
||||||
if (!params)
|
|
||||||
return 0;
|
|
||||||
|
|
||||||
tmp_options = kstrdup(params, GFP_KERNEL);
|
|
||||||
if (!tmp_options) {
|
|
||||||
p9_debug(P9_DEBUG_ERROR,
|
|
||||||
"failed to allocate copy of option string\n");
|
|
||||||
return -ENOMEM;
|
|
||||||
}
|
|
||||||
options = tmp_options;
|
|
||||||
|
|
||||||
while ((p = strsep(&options, ",")) != NULL) {
|
|
||||||
int token;
|
|
||||||
int r;
|
|
||||||
if (!*p)
|
|
||||||
continue;
|
|
||||||
token = match_token(p, tokens, args);
|
|
||||||
if ((token != Opt_err) && (token != Opt_privport)) {
|
|
||||||
r = match_int(&args[0], &option);
|
|
||||||
if (r < 0) {
|
|
||||||
p9_debug(P9_DEBUG_ERROR,
|
|
||||||
"integer field, but no integer?\n");
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
switch (token) {
|
|
||||||
case Opt_port:
|
|
||||||
opts->port = option;
|
|
||||||
break;
|
|
||||||
case Opt_sq_depth:
|
|
||||||
opts->sq_depth = option;
|
|
||||||
break;
|
|
||||||
case Opt_rq_depth:
|
|
||||||
opts->rq_depth = option;
|
|
||||||
break;
|
|
||||||
case Opt_timeout:
|
|
||||||
opts->timeout = option;
|
|
||||||
break;
|
|
||||||
case Opt_privport:
|
|
||||||
opts->privport = 1;
|
|
||||||
break;
|
|
||||||
default:
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
/* RQ must be at least as large as the SQ */
|
|
||||||
opts->rq_depth = max(opts->rq_depth, opts->sq_depth);
|
|
||||||
kfree(tmp_options);
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
static int
|
|
||||||
p9_cm_event_handler(struct rdma_cm_id *id, struct rdma_cm_event *event)
|
|
||||||
{
|
|
||||||
struct p9_client *c = id->context;
|
|
||||||
struct p9_trans_rdma *rdma = c->trans;
|
|
||||||
switch (event->event) {
|
|
||||||
case RDMA_CM_EVENT_ADDR_RESOLVED:
|
|
||||||
BUG_ON(rdma->state != P9_RDMA_INIT);
|
|
||||||
rdma->state = P9_RDMA_ADDR_RESOLVED;
|
|
||||||
break;
|
|
||||||
|
|
||||||
case RDMA_CM_EVENT_ROUTE_RESOLVED:
|
|
||||||
BUG_ON(rdma->state != P9_RDMA_ADDR_RESOLVED);
|
|
||||||
rdma->state = P9_RDMA_ROUTE_RESOLVED;
|
|
||||||
break;
|
|
||||||
|
|
||||||
case RDMA_CM_EVENT_ESTABLISHED:
|
|
||||||
BUG_ON(rdma->state != P9_RDMA_ROUTE_RESOLVED);
|
|
||||||
rdma->state = P9_RDMA_CONNECTED;
|
|
||||||
break;
|
|
||||||
|
|
||||||
case RDMA_CM_EVENT_DISCONNECTED:
|
|
||||||
if (rdma)
|
|
||||||
rdma->state = P9_RDMA_CLOSED;
|
|
||||||
if (c)
|
|
||||||
c->status = Disconnected;
|
|
||||||
break;
|
|
||||||
|
|
||||||
case RDMA_CM_EVENT_TIMEWAIT_EXIT:
|
|
||||||
break;
|
|
||||||
|
|
||||||
case RDMA_CM_EVENT_ADDR_CHANGE:
|
|
||||||
case RDMA_CM_EVENT_ROUTE_ERROR:
|
|
||||||
case RDMA_CM_EVENT_DEVICE_REMOVAL:
|
|
||||||
case RDMA_CM_EVENT_MULTICAST_JOIN:
|
|
||||||
case RDMA_CM_EVENT_MULTICAST_ERROR:
|
|
||||||
case RDMA_CM_EVENT_REJECTED:
|
|
||||||
case RDMA_CM_EVENT_CONNECT_REQUEST:
|
|
||||||
case RDMA_CM_EVENT_CONNECT_RESPONSE:
|
|
||||||
case RDMA_CM_EVENT_CONNECT_ERROR:
|
|
||||||
case RDMA_CM_EVENT_ADDR_ERROR:
|
|
||||||
case RDMA_CM_EVENT_UNREACHABLE:
|
|
||||||
c->status = Disconnected;
|
|
||||||
rdma_disconnect(rdma->cm_id);
|
|
||||||
break;
|
|
||||||
default:
|
|
||||||
BUG();
|
|
||||||
}
|
|
||||||
complete(&rdma->cm_done);
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
static void
|
|
||||||
handle_recv(struct p9_client *client, struct p9_trans_rdma *rdma,
|
|
||||||
struct p9_rdma_context *c, enum ib_wc_status status, u32 byte_len)
|
|
||||||
{
|
|
||||||
struct p9_req_t *req;
|
|
||||||
int err = 0;
|
|
||||||
int16_t tag;
|
|
||||||
|
|
||||||
req = NULL;
|
|
||||||
ib_dma_unmap_single(rdma->cm_id->device, c->busa, client->msize,
|
|
||||||
DMA_FROM_DEVICE);
|
|
||||||
|
|
||||||
if (status != IB_WC_SUCCESS)
|
|
||||||
goto err_out;
|
|
||||||
|
|
||||||
err = p9_parse_header(c->rc, NULL, NULL, &tag, 1);
|
|
||||||
if (err)
|
|
||||||
goto err_out;
|
|
||||||
|
|
||||||
req = p9_tag_lookup(client, tag);
|
|
||||||
if (!req)
|
|
||||||
goto err_out;
|
|
||||||
|
|
||||||
/* Check that we have not yet received a reply for this request.
|
|
||||||
*/
|
|
||||||
if (unlikely(req->rc)) {
|
|
||||||
pr_err("Duplicate reply for request %d", tag);
|
|
||||||
goto err_out;
|
|
||||||
}
|
|
||||||
|
|
||||||
req->rc = c->rc;
|
|
||||||
p9_client_cb(client, req, REQ_STATUS_RCVD);
|
|
||||||
|
|
||||||
return;
|
|
||||||
|
|
||||||
err_out:
|
|
||||||
p9_debug(P9_DEBUG_ERROR, "req %p err %d status %d\n", req, err, status);
|
|
||||||
rdma->state = P9_RDMA_FLUSHING;
|
|
||||||
client->status = Disconnected;
|
|
||||||
}
|
|
||||||
|
|
||||||
static void
|
|
||||||
handle_send(struct p9_client *client, struct p9_trans_rdma *rdma,
|
|
||||||
struct p9_rdma_context *c, enum ib_wc_status status, u32 byte_len)
|
|
||||||
{
|
|
||||||
ib_dma_unmap_single(rdma->cm_id->device,
|
|
||||||
c->busa, c->req->tc->size,
|
|
||||||
DMA_TO_DEVICE);
|
|
||||||
}
|
|
||||||
|
|
||||||
static void qp_event_handler(struct ib_event *event, void *context)
|
|
||||||
{
|
|
||||||
p9_debug(P9_DEBUG_ERROR, "QP event %d context %p\n",
|
|
||||||
event->event, context);
|
|
||||||
}
|
|
||||||
|
|
||||||
static void cq_comp_handler(struct ib_cq *cq, void *cq_context)
|
|
||||||
{
|
|
||||||
struct p9_client *client = cq_context;
|
|
||||||
struct p9_trans_rdma *rdma = client->trans;
|
|
||||||
int ret;
|
|
||||||
struct ib_wc wc;
|
|
||||||
|
|
||||||
ib_req_notify_cq(rdma->cq, IB_CQ_NEXT_COMP);
|
|
||||||
while ((ret = ib_poll_cq(cq, 1, &wc)) > 0) {
|
|
||||||
struct p9_rdma_context *c = (void *) (unsigned long) wc.wr_id;
|
|
||||||
|
|
||||||
switch (c->wc_op) {
|
|
||||||
case IB_WC_RECV:
|
|
||||||
handle_recv(client, rdma, c, wc.status, wc.byte_len);
|
|
||||||
up(&rdma->rq_sem);
|
|
||||||
break;
|
|
||||||
|
|
||||||
case IB_WC_SEND:
|
|
||||||
handle_send(client, rdma, c, wc.status, wc.byte_len);
|
|
||||||
up(&rdma->sq_sem);
|
|
||||||
break;
|
|
||||||
|
|
||||||
default:
|
|
||||||
pr_err("unexpected completion type, c->wc_op=%d, wc.opcode=%d, status=%d\n",
|
|
||||||
c->wc_op, wc.opcode, wc.status);
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
kfree(c);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
static void cq_event_handler(struct ib_event *e, void *v)
|
|
||||||
{
|
|
||||||
p9_debug(P9_DEBUG_ERROR, "CQ event %d context %p\n", e->event, v);
|
|
||||||
}
|
|
||||||
|
|
||||||
static void rdma_destroy_trans(struct p9_trans_rdma *rdma)
|
|
||||||
{
|
|
||||||
if (!rdma)
|
|
||||||
return;
|
|
||||||
|
|
||||||
if (rdma->qp && !IS_ERR(rdma->qp))
|
|
||||||
ib_destroy_qp(rdma->qp);
|
|
||||||
|
|
||||||
if (rdma->pd && !IS_ERR(rdma->pd))
|
|
||||||
ib_dealloc_pd(rdma->pd);
|
|
||||||
|
|
||||||
if (rdma->cq && !IS_ERR(rdma->cq))
|
|
||||||
ib_destroy_cq(rdma->cq);
|
|
||||||
|
|
||||||
if (rdma->cm_id && !IS_ERR(rdma->cm_id))
|
|
||||||
rdma_destroy_id(rdma->cm_id);
|
|
||||||
|
|
||||||
kfree(rdma);
|
|
||||||
}
|
|
||||||
|
|
||||||
static int
|
|
||||||
post_recv(struct p9_client *client, struct p9_rdma_context *c)
|
|
||||||
{
|
|
||||||
struct p9_trans_rdma *rdma = client->trans;
|
|
||||||
struct ib_recv_wr wr, *bad_wr;
|
|
||||||
struct ib_sge sge;
|
|
||||||
|
|
||||||
c->busa = ib_dma_map_single(rdma->cm_id->device,
|
|
||||||
c->rc->sdata, client->msize,
|
|
||||||
DMA_FROM_DEVICE);
|
|
||||||
if (ib_dma_mapping_error(rdma->cm_id->device, c->busa))
|
|
||||||
goto error;
|
|
||||||
|
|
||||||
sge.addr = c->busa;
|
|
||||||
sge.length = client->msize;
|
|
||||||
sge.lkey = rdma->pd->local_dma_lkey;
|
|
||||||
|
|
||||||
wr.next = NULL;
|
|
||||||
c->wc_op = IB_WC_RECV;
|
|
||||||
wr.wr_id = (unsigned long) c;
|
|
||||||
wr.sg_list = &sge;
|
|
||||||
wr.num_sge = 1;
|
|
||||||
return ib_post_recv(rdma->qp, &wr, &bad_wr);
|
|
||||||
|
|
||||||
error:
|
|
||||||
p9_debug(P9_DEBUG_ERROR, "EIO\n");
|
|
||||||
return -EIO;
|
|
||||||
}
|
|
||||||
|
|
||||||
static int rdma_request(struct p9_client *client, struct p9_req_t *req)
|
|
||||||
{
|
|
||||||
struct p9_trans_rdma *rdma = client->trans;
|
|
||||||
struct ib_send_wr wr, *bad_wr;
|
|
||||||
struct ib_sge sge;
|
|
||||||
int err = 0;
|
|
||||||
unsigned long flags;
|
|
||||||
struct p9_rdma_context *c = NULL;
|
|
||||||
struct p9_rdma_context *rpl_context = NULL;
|
|
||||||
|
|
||||||
/* When an error occurs between posting the recv and the send,
|
|
||||||
* there will be a receive context posted without a pending request.
|
|
||||||
* Since there is no way to "un-post" it, we remember it and skip
|
|
||||||
* post_recv() for the next request.
|
|
||||||
* So here,
|
|
||||||
* see if we are this `next request' and need to absorb an excess rc.
|
|
||||||
* If yes, then drop and free our own, and do not recv_post().
|
|
||||||
**/
|
|
||||||
if (unlikely(atomic_read(&rdma->excess_rc) > 0)) {
|
|
||||||
if ((atomic_sub_return(1, &rdma->excess_rc) >= 0)) {
|
|
||||||
/* Got one ! */
|
|
||||||
kfree(req->rc);
|
|
||||||
req->rc = NULL;
|
|
||||||
goto dont_need_post_recv;
|
|
||||||
} else {
|
|
||||||
/* We raced and lost. */
|
|
||||||
atomic_inc(&rdma->excess_rc);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/* Allocate an fcall for the reply */
|
|
||||||
rpl_context = kmalloc(sizeof *rpl_context, GFP_NOFS);
|
|
||||||
if (!rpl_context) {
|
|
||||||
err = -ENOMEM;
|
|
||||||
goto recv_error;
|
|
||||||
}
|
|
||||||
rpl_context->rc = req->rc;
|
|
||||||
|
|
||||||
/*
|
|
||||||
* Post a receive buffer for this request. We need to ensure
|
|
||||||
* there is a reply buffer available for every outstanding
|
|
||||||
* request. A flushed request can result in no reply for an
|
|
||||||
* outstanding request, so we must keep a count to avoid
|
|
||||||
* overflowing the RQ.
|
|
||||||
*/
|
|
||||||
if (down_interruptible(&rdma->rq_sem)) {
|
|
||||||
err = -EINTR;
|
|
||||||
goto recv_error;
|
|
||||||
}
|
|
||||||
|
|
||||||
err = post_recv(client, rpl_context);
|
|
||||||
if (err) {
|
|
||||||
p9_debug(P9_DEBUG_FCALL, "POST RECV failed\n");
|
|
||||||
goto recv_error;
|
|
||||||
}
|
|
||||||
/* remove posted receive buffer from request structure */
|
|
||||||
req->rc = NULL;
|
|
||||||
|
|
||||||
dont_need_post_recv:
|
|
||||||
/* Post the request */
|
|
||||||
c = kmalloc(sizeof *c, GFP_NOFS);
|
|
||||||
if (!c) {
|
|
||||||
err = -ENOMEM;
|
|
||||||
goto send_error;
|
|
||||||
}
|
|
||||||
c->req = req;
|
|
||||||
|
|
||||||
c->busa = ib_dma_map_single(rdma->cm_id->device,
|
|
||||||
c->req->tc->sdata, c->req->tc->size,
|
|
||||||
DMA_TO_DEVICE);
|
|
||||||
if (ib_dma_mapping_error(rdma->cm_id->device, c->busa)) {
|
|
||||||
err = -EIO;
|
|
||||||
goto send_error;
|
|
||||||
}
|
|
||||||
|
|
||||||
sge.addr = c->busa;
|
|
||||||
sge.length = c->req->tc->size;
|
|
||||||
sge.lkey = rdma->pd->local_dma_lkey;
|
|
||||||
|
|
||||||
wr.next = NULL;
|
|
||||||
c->wc_op = IB_WC_SEND;
|
|
||||||
wr.wr_id = (unsigned long) c;
|
|
||||||
wr.opcode = IB_WR_SEND;
|
|
||||||
wr.send_flags = IB_SEND_SIGNALED;
|
|
||||||
wr.sg_list = &sge;
|
|
||||||
wr.num_sge = 1;
|
|
||||||
|
|
||||||
if (down_interruptible(&rdma->sq_sem)) {
|
|
||||||
err = -EINTR;
|
|
||||||
goto send_error;
|
|
||||||
}
|
|
||||||
|
|
||||||
/* Mark request as `sent' *before* we actually send it,
|
|
||||||
* because doing if after could erase the REQ_STATUS_RCVD
|
|
||||||
* status in case of a very fast reply.
|
|
||||||
*/
|
|
||||||
req->status = REQ_STATUS_SENT;
|
|
||||||
err = ib_post_send(rdma->qp, &wr, &bad_wr);
|
|
||||||
if (err)
|
|
||||||
goto send_error;
|
|
||||||
|
|
||||||
/* Success */
|
|
||||||
return 0;
|
|
||||||
|
|
||||||
/* Handle errors that happened during or while preparing the send: */
|
|
||||||
send_error:
|
|
||||||
req->status = REQ_STATUS_ERROR;
|
|
||||||
kfree(c);
|
|
||||||
p9_debug(P9_DEBUG_ERROR, "Error %d in rdma_request()\n", err);
|
|
||||||
|
|
||||||
/* Ach.
|
|
||||||
* We did recv_post(), but not send. We have one recv_post in excess.
|
|
||||||
*/
|
|
||||||
atomic_inc(&rdma->excess_rc);
|
|
||||||
return err;
|
|
||||||
|
|
||||||
/* Handle errors that happened during or while preparing post_recv(): */
|
|
||||||
recv_error:
|
|
||||||
kfree(rpl_context);
|
|
||||||
spin_lock_irqsave(&rdma->req_lock, flags);
|
|
||||||
if (rdma->state < P9_RDMA_CLOSING) {
|
|
||||||
rdma->state = P9_RDMA_CLOSING;
|
|
||||||
spin_unlock_irqrestore(&rdma->req_lock, flags);
|
|
||||||
rdma_disconnect(rdma->cm_id);
|
|
||||||
} else
|
|
||||||
spin_unlock_irqrestore(&rdma->req_lock, flags);
|
|
||||||
return err;
|
|
||||||
}
|
|
||||||
|
|
||||||
static void rdma_close(struct p9_client *client)
|
|
||||||
{
|
|
||||||
struct p9_trans_rdma *rdma;
|
|
||||||
|
|
||||||
if (!client)
|
|
||||||
return;
|
|
||||||
|
|
||||||
rdma = client->trans;
|
|
||||||
if (!rdma)
|
|
||||||
return;
|
|
||||||
|
|
||||||
client->status = Disconnected;
|
|
||||||
rdma_disconnect(rdma->cm_id);
|
|
||||||
rdma_destroy_trans(rdma);
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* alloc_rdma - Allocate and initialize the rdma transport structure
|
|
||||||
* @opts: Mount options structure
|
|
||||||
*/
|
|
||||||
static struct p9_trans_rdma *alloc_rdma(struct p9_rdma_opts *opts)
|
|
||||||
{
|
|
||||||
struct p9_trans_rdma *rdma;
|
|
||||||
|
|
||||||
rdma = kzalloc(sizeof(struct p9_trans_rdma), GFP_KERNEL);
|
|
||||||
if (!rdma)
|
|
||||||
return NULL;
|
|
||||||
|
|
||||||
rdma->sq_depth = opts->sq_depth;
|
|
||||||
rdma->rq_depth = opts->rq_depth;
|
|
||||||
rdma->timeout = opts->timeout;
|
|
||||||
spin_lock_init(&rdma->req_lock);
|
|
||||||
init_completion(&rdma->cm_done);
|
|
||||||
sema_init(&rdma->sq_sem, rdma->sq_depth);
|
|
||||||
sema_init(&rdma->rq_sem, rdma->rq_depth);
|
|
||||||
atomic_set(&rdma->excess_rc, 0);
|
|
||||||
|
|
||||||
return rdma;
|
|
||||||
}
|
|
||||||
|
|
||||||
static int rdma_cancel(struct p9_client *client, struct p9_req_t *req)
|
|
||||||
{
|
|
||||||
/* Nothing to do here.
|
|
||||||
* We will take care of it (if we have to) in rdma_cancelled()
|
|
||||||
*/
|
|
||||||
return 1;
|
|
||||||
}
|
|
||||||
|
|
||||||
/* A request has been fully flushed without a reply.
|
|
||||||
* That means we have posted one buffer in excess.
|
|
||||||
*/
|
|
||||||
static int rdma_cancelled(struct p9_client *client, struct p9_req_t *req)
|
|
||||||
{
|
|
||||||
struct p9_trans_rdma *rdma = client->trans;
|
|
||||||
atomic_inc(&rdma->excess_rc);
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
static int p9_rdma_bind_privport(struct p9_trans_rdma *rdma)
|
|
||||||
{
|
|
||||||
struct sockaddr_in cl = {
|
|
||||||
.sin_family = AF_INET,
|
|
||||||
.sin_addr.s_addr = htonl(INADDR_ANY),
|
|
||||||
};
|
|
||||||
int port, err = -EINVAL;
|
|
||||||
|
|
||||||
for (port = P9_DEF_MAX_RESVPORT; port >= P9_DEF_MIN_RESVPORT; port--) {
|
|
||||||
cl.sin_port = htons((ushort)port);
|
|
||||||
err = rdma_bind_addr(rdma->cm_id, (struct sockaddr *)&cl);
|
|
||||||
if (err != -EADDRINUSE)
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
return err;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* trans_create_rdma - Transport method for creating atransport instance
|
|
||||||
* @client: client instance
|
|
||||||
* @addr: IP address string
|
|
||||||
* @args: Mount options string
|
|
||||||
*/
|
|
||||||
static int
|
|
||||||
rdma_create_trans(struct p9_client *client, const char *addr, char *args)
|
|
||||||
{
|
|
||||||
int err;
|
|
||||||
struct p9_rdma_opts opts;
|
|
||||||
struct p9_trans_rdma *rdma;
|
|
||||||
struct rdma_conn_param conn_param;
|
|
||||||
struct ib_qp_init_attr qp_attr;
|
|
||||||
struct ib_cq_init_attr cq_attr = {};
|
|
||||||
|
|
||||||
if (addr == NULL)
|
|
||||||
return -EINVAL;
|
|
||||||
|
|
||||||
/* Parse the transport specific mount options */
|
|
||||||
err = parse_opts(args, &opts);
|
|
||||||
if (err < 0)
|
|
||||||
return err;
|
|
||||||
|
|
||||||
/* Create and initialize the RDMA transport structure */
|
|
||||||
rdma = alloc_rdma(&opts);
|
|
||||||
if (!rdma)
|
|
||||||
return -ENOMEM;
|
|
||||||
|
|
||||||
/* Create the RDMA CM ID */
|
|
||||||
rdma->cm_id = rdma_create_id(&init_net, p9_cm_event_handler, client,
|
|
||||||
RDMA_PS_TCP, IB_QPT_RC);
|
|
||||||
if (IS_ERR(rdma->cm_id))
|
|
||||||
goto error;
|
|
||||||
|
|
||||||
/* Associate the client with the transport */
|
|
||||||
client->trans = rdma;
|
|
||||||
|
|
||||||
/* Bind to a privileged port if we need to */
|
|
||||||
if (opts.privport) {
|
|
||||||
err = p9_rdma_bind_privport(rdma);
|
|
||||||
if (err < 0) {
|
|
||||||
pr_err("%s (%d): problem binding to privport: %d\n",
|
|
||||||
__func__, task_pid_nr(current), -err);
|
|
||||||
goto error;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/* Resolve the server's address */
|
|
||||||
rdma->addr.sin_family = AF_INET;
|
|
||||||
rdma->addr.sin_addr.s_addr = in_aton(addr);
|
|
||||||
rdma->addr.sin_port = htons(opts.port);
|
|
||||||
err = rdma_resolve_addr(rdma->cm_id, NULL,
|
|
||||||
(struct sockaddr *)&rdma->addr,
|
|
||||||
rdma->timeout);
|
|
||||||
if (err)
|
|
||||||
goto error;
|
|
||||||
err = wait_for_completion_interruptible(&rdma->cm_done);
|
|
||||||
if (err || (rdma->state != P9_RDMA_ADDR_RESOLVED))
|
|
||||||
goto error;
|
|
||||||
|
|
||||||
/* Resolve the route to the server */
|
|
||||||
err = rdma_resolve_route(rdma->cm_id, rdma->timeout);
|
|
||||||
if (err)
|
|
||||||
goto error;
|
|
||||||
err = wait_for_completion_interruptible(&rdma->cm_done);
|
|
||||||
if (err || (rdma->state != P9_RDMA_ROUTE_RESOLVED))
|
|
||||||
goto error;
|
|
||||||
|
|
||||||
/* Create the Completion Queue */
|
|
||||||
cq_attr.cqe = opts.sq_depth + opts.rq_depth + 1;
|
|
||||||
rdma->cq = ib_create_cq(rdma->cm_id->device, cq_comp_handler,
|
|
||||||
cq_event_handler, client,
|
|
||||||
&cq_attr);
|
|
||||||
if (IS_ERR(rdma->cq))
|
|
||||||
goto error;
|
|
||||||
ib_req_notify_cq(rdma->cq, IB_CQ_NEXT_COMP);
|
|
||||||
|
|
||||||
/* Create the Protection Domain */
|
|
||||||
rdma->pd = ib_alloc_pd(rdma->cm_id->device);
|
|
||||||
if (IS_ERR(rdma->pd))
|
|
||||||
goto error;
|
|
||||||
|
|
||||||
/* Create the Queue Pair */
|
|
||||||
memset(&qp_attr, 0, sizeof qp_attr);
|
|
||||||
qp_attr.event_handler = qp_event_handler;
|
|
||||||
qp_attr.qp_context = client;
|
|
||||||
qp_attr.cap.max_send_wr = opts.sq_depth;
|
|
||||||
qp_attr.cap.max_recv_wr = opts.rq_depth;
|
|
||||||
qp_attr.cap.max_send_sge = P9_RDMA_SEND_SGE;
|
|
||||||
qp_attr.cap.max_recv_sge = P9_RDMA_RECV_SGE;
|
|
||||||
qp_attr.sq_sig_type = IB_SIGNAL_REQ_WR;
|
|
||||||
qp_attr.qp_type = IB_QPT_RC;
|
|
||||||
qp_attr.send_cq = rdma->cq;
|
|
||||||
qp_attr.recv_cq = rdma->cq;
|
|
||||||
err = rdma_create_qp(rdma->cm_id, rdma->pd, &qp_attr);
|
|
||||||
if (err)
|
|
||||||
goto error;
|
|
||||||
rdma->qp = rdma->cm_id->qp;
|
|
||||||
|
|
||||||
/* Request a connection */
|
|
||||||
memset(&conn_param, 0, sizeof(conn_param));
|
|
||||||
conn_param.private_data = NULL;
|
|
||||||
conn_param.private_data_len = 0;
|
|
||||||
conn_param.responder_resources = P9_RDMA_IRD;
|
|
||||||
conn_param.initiator_depth = P9_RDMA_ORD;
|
|
||||||
err = rdma_connect(rdma->cm_id, &conn_param);
|
|
||||||
if (err)
|
|
||||||
goto error;
|
|
||||||
err = wait_for_completion_interruptible(&rdma->cm_done);
|
|
||||||
if (err || (rdma->state != P9_RDMA_CONNECTED))
|
|
||||||
goto error;
|
|
||||||
|
|
||||||
client->status = Connected;
|
|
||||||
|
|
||||||
return 0;
|
|
||||||
|
|
||||||
error:
|
|
||||||
rdma_destroy_trans(rdma);
|
|
||||||
return -ENOTCONN;
|
|
||||||
}
|
|
||||||
|
|
||||||
static struct p9_trans_module p9_rdma_trans = {
|
|
||||||
.name = "rdma",
|
|
||||||
.maxsize = P9_RDMA_MAXSIZE,
|
|
||||||
.def = 0,
|
|
||||||
.owner = THIS_MODULE,
|
|
||||||
.create = rdma_create_trans,
|
|
||||||
.close = rdma_close,
|
|
||||||
.request = rdma_request,
|
|
||||||
.cancel = rdma_cancel,
|
|
||||||
.cancelled = rdma_cancelled,
|
|
||||||
};
|
|
||||||
|
|
||||||
/**
|
|
||||||
* p9_trans_rdma_init - Register the 9P RDMA transport driver
|
|
||||||
*/
|
|
||||||
static int __init p9_trans_rdma_init(void)
|
|
||||||
{
|
|
||||||
v9fs_register_trans(&p9_rdma_trans);
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
static void __exit p9_trans_rdma_exit(void)
|
|
||||||
{
|
|
||||||
v9fs_unregister_trans(&p9_rdma_trans);
|
|
||||||
}
|
|
||||||
|
|
||||||
module_init(p9_trans_rdma_init);
|
|
||||||
module_exit(p9_trans_rdma_exit);
|
|
||||||
|
|
||||||
MODULE_AUTHOR("Tom Tucker <tom@opengridcomputing.com>");
|
|
||||||
MODULE_DESCRIPTION("RDMA Transport for 9P");
|
|
||||||
MODULE_LICENSE("Dual BSD/GPL");
|
|
@ -1,788 +0,0 @@
|
|||||||
/*
|
|
||||||
* The Virtio 9p transport driver
|
|
||||||
*
|
|
||||||
* This is a block based transport driver based on the lguest block driver
|
|
||||||
* code.
|
|
||||||
*
|
|
||||||
* Copyright (C) 2007, 2008 Eric Van Hensbergen, IBM Corporation
|
|
||||||
*
|
|
||||||
* Based on virtio console driver
|
|
||||||
* Copyright (C) 2006, 2007 Rusty Russell, IBM Corporation
|
|
||||||
*
|
|
||||||
* This program is free software; you can redistribute it and/or modify
|
|
||||||
* it under the terms of the GNU General Public License version 2
|
|
||||||
* as published by the Free Software Foundation.
|
|
||||||
*
|
|
||||||
* This program is distributed in the hope that it will be useful,
|
|
||||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
||||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
||||||
* GNU General Public License for more details.
|
|
||||||
*
|
|
||||||
* You should have received a copy of the GNU General Public License
|
|
||||||
* along with this program; if not, write to:
|
|
||||||
* Free Software Foundation
|
|
||||||
* 51 Franklin Street, Fifth Floor
|
|
||||||
* Boston, MA 02111-1301 USA
|
|
||||||
*
|
|
||||||
*/
|
|
||||||
|
|
||||||
#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
|
|
||||||
|
|
||||||
#include <linux/in.h>
|
|
||||||
#include <linux/module.h>
|
|
||||||
#include <linux/net.h>
|
|
||||||
#include <linux/ipv6.h>
|
|
||||||
#include <linux/errno.h>
|
|
||||||
#include <linux/kernel.h>
|
|
||||||
#include <linux/un.h>
|
|
||||||
#include <linux/uaccess.h>
|
|
||||||
#include <linux/inet.h>
|
|
||||||
#include <linux/idr.h>
|
|
||||||
#include <linux/file.h>
|
|
||||||
#include <linux/highmem.h>
|
|
||||||
#include <linux/slab.h>
|
|
||||||
#include <net/9p/9p.h>
|
|
||||||
#include <linux/parser.h>
|
|
||||||
#include <net/9p/client.h>
|
|
||||||
#include <net/9p/transport.h>
|
|
||||||
#include <linux/scatterlist.h>
|
|
||||||
#include <linux/swap.h>
|
|
||||||
#include <linux/virtio.h>
|
|
||||||
#include <linux/virtio_9p.h>
|
|
||||||
#include "trans_common.h"
|
|
||||||
|
|
||||||
#define VIRTQUEUE_NUM 128
|
|
||||||
|
|
||||||
/* a single mutex to manage channel initialization and attachment */
|
|
||||||
static DEFINE_MUTEX(virtio_9p_lock);
|
|
||||||
static DECLARE_WAIT_QUEUE_HEAD(vp_wq);
|
|
||||||
static atomic_t vp_pinned = ATOMIC_INIT(0);
|
|
||||||
|
|
||||||
/**
|
|
||||||
* struct virtio_chan - per-instance transport information
|
|
||||||
* @initialized: whether the channel is initialized
|
|
||||||
* @inuse: whether the channel is in use
|
|
||||||
* @lock: protects multiple elements within this structure
|
|
||||||
* @client: client instance
|
|
||||||
* @vdev: virtio dev associated with this channel
|
|
||||||
* @vq: virtio queue associated with this channel
|
|
||||||
* @sg: scatter gather list which is used to pack a request (protected?)
|
|
||||||
*
|
|
||||||
* We keep all per-channel information in a structure.
|
|
||||||
* This structure is allocated within the devices dev->mem space.
|
|
||||||
* A pointer to the structure will get put in the transport private.
|
|
||||||
*
|
|
||||||
*/
|
|
||||||
|
|
||||||
struct virtio_chan {
|
|
||||||
bool inuse;
|
|
||||||
|
|
||||||
spinlock_t lock;
|
|
||||||
|
|
||||||
struct p9_client *client;
|
|
||||||
struct virtio_device *vdev;
|
|
||||||
struct virtqueue *vq;
|
|
||||||
int ring_bufs_avail;
|
|
||||||
wait_queue_head_t *vc_wq;
|
|
||||||
/* This is global limit. Since we don't have a global structure,
|
|
||||||
* will be placing it in each channel.
|
|
||||||
*/
|
|
||||||
unsigned long p9_max_pages;
|
|
||||||
/* Scatterlist: can be too big for stack. */
|
|
||||||
struct scatterlist sg[VIRTQUEUE_NUM];
|
|
||||||
|
|
||||||
int tag_len;
|
|
||||||
/*
|
|
||||||
* tag name to identify a mount Non-null terminated
|
|
||||||
*/
|
|
||||||
char *tag;
|
|
||||||
|
|
||||||
struct list_head chan_list;
|
|
||||||
};
|
|
||||||
|
|
||||||
static struct list_head virtio_chan_list;
|
|
||||||
|
|
||||||
/* How many bytes left in this page. */
|
|
||||||
static unsigned int rest_of_page(void *data)
|
|
||||||
{
|
|
||||||
return PAGE_SIZE - ((unsigned long)data % PAGE_SIZE);
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* p9_virtio_close - reclaim resources of a channel
|
|
||||||
* @client: client instance
|
|
||||||
*
|
|
||||||
* This reclaims a channel by freeing its resources and
|
|
||||||
* reseting its inuse flag.
|
|
||||||
*
|
|
||||||
*/
|
|
||||||
|
|
||||||
static void p9_virtio_close(struct p9_client *client)
|
|
||||||
{
|
|
||||||
struct virtio_chan *chan = client->trans;
|
|
||||||
|
|
||||||
mutex_lock(&virtio_9p_lock);
|
|
||||||
if (chan)
|
|
||||||
chan->inuse = false;
|
|
||||||
mutex_unlock(&virtio_9p_lock);
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* req_done - callback which signals activity from the server
|
|
||||||
* @vq: virtio queue activity was received on
|
|
||||||
*
|
|
||||||
* This notifies us that the server has triggered some activity
|
|
||||||
* on the virtio channel - most likely a response to request we
|
|
||||||
* sent. Figure out which requests now have responses and wake up
|
|
||||||
* those threads.
|
|
||||||
*
|
|
||||||
* Bugs: could do with some additional sanity checking, but appears to work.
|
|
||||||
*
|
|
||||||
*/
|
|
||||||
|
|
||||||
static void req_done(struct virtqueue *vq)
|
|
||||||
{
|
|
||||||
struct virtio_chan *chan = vq->vdev->priv;
|
|
||||||
struct p9_fcall *rc;
|
|
||||||
unsigned int len;
|
|
||||||
struct p9_req_t *req;
|
|
||||||
unsigned long flags;
|
|
||||||
|
|
||||||
p9_debug(P9_DEBUG_TRANS, ": request done\n");
|
|
||||||
|
|
||||||
while (1) {
|
|
||||||
spin_lock_irqsave(&chan->lock, flags);
|
|
||||||
rc = virtqueue_get_buf(chan->vq, &len);
|
|
||||||
if (rc == NULL) {
|
|
||||||
spin_unlock_irqrestore(&chan->lock, flags);
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
chan->ring_bufs_avail = 1;
|
|
||||||
spin_unlock_irqrestore(&chan->lock, flags);
|
|
||||||
/* Wakeup if anyone waiting for VirtIO ring space. */
|
|
||||||
wake_up(chan->vc_wq);
|
|
||||||
p9_debug(P9_DEBUG_TRANS, ": rc %p\n", rc);
|
|
||||||
p9_debug(P9_DEBUG_TRANS, ": lookup tag %d\n", rc->tag);
|
|
||||||
req = p9_tag_lookup(chan->client, rc->tag);
|
|
||||||
p9_client_cb(chan->client, req, REQ_STATUS_RCVD);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* pack_sg_list - pack a scatter gather list from a linear buffer
|
|
||||||
* @sg: scatter/gather list to pack into
|
|
||||||
* @start: which segment of the sg_list to start at
|
|
||||||
* @limit: maximum segment to pack data to
|
|
||||||
* @data: data to pack into scatter/gather list
|
|
||||||
* @count: amount of data to pack into the scatter/gather list
|
|
||||||
*
|
|
||||||
* sg_lists have multiple segments of various sizes. This will pack
|
|
||||||
* arbitrary data into an existing scatter gather list, segmenting the
|
|
||||||
* data as necessary within constraints.
|
|
||||||
*
|
|
||||||
*/
|
|
||||||
|
|
||||||
static int pack_sg_list(struct scatterlist *sg, int start,
|
|
||||||
int limit, char *data, int count)
|
|
||||||
{
|
|
||||||
int s;
|
|
||||||
int index = start;
|
|
||||||
|
|
||||||
while (count) {
|
|
||||||
s = rest_of_page(data);
|
|
||||||
if (s > count)
|
|
||||||
s = count;
|
|
||||||
BUG_ON(index >= limit);
|
|
||||||
/* Make sure we don't terminate early. */
|
|
||||||
sg_unmark_end(&sg[index]);
|
|
||||||
sg_set_buf(&sg[index++], data, s);
|
|
||||||
count -= s;
|
|
||||||
data += s;
|
|
||||||
}
|
|
||||||
if (index-start)
|
|
||||||
sg_mark_end(&sg[index - 1]);
|
|
||||||
return index-start;
|
|
||||||
}
|
|
||||||
|
|
||||||
/* We don't currently allow canceling of virtio requests */
|
|
||||||
static int p9_virtio_cancel(struct p9_client *client, struct p9_req_t *req)
|
|
||||||
{
|
|
||||||
return 1;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* pack_sg_list_p - Just like pack_sg_list. Instead of taking a buffer,
|
|
||||||
* this takes a list of pages.
|
|
||||||
* @sg: scatter/gather list to pack into
|
|
||||||
* @start: which segment of the sg_list to start at
|
|
||||||
* @pdata: a list of pages to add into sg.
|
|
||||||
* @nr_pages: number of pages to pack into the scatter/gather list
|
|
||||||
* @offs: amount of data in the beginning of first page _not_ to pack
|
|
||||||
* @count: amount of data to pack into the scatter/gather list
|
|
||||||
*/
|
|
||||||
static int
|
|
||||||
pack_sg_list_p(struct scatterlist *sg, int start, int limit,
|
|
||||||
struct page **pdata, int nr_pages, size_t offs, int count)
|
|
||||||
{
|
|
||||||
int i = 0, s;
|
|
||||||
int data_off = offs;
|
|
||||||
int index = start;
|
|
||||||
|
|
||||||
BUG_ON(nr_pages > (limit - start));
|
|
||||||
/*
|
|
||||||
* if the first page doesn't start at
|
|
||||||
* page boundary find the offset
|
|
||||||
*/
|
|
||||||
while (nr_pages) {
|
|
||||||
s = PAGE_SIZE - data_off;
|
|
||||||
if (s > count)
|
|
||||||
s = count;
|
|
||||||
BUG_ON(index >= limit);
|
|
||||||
/* Make sure we don't terminate early. */
|
|
||||||
sg_unmark_end(&sg[index]);
|
|
||||||
sg_set_page(&sg[index++], pdata[i++], s, data_off);
|
|
||||||
data_off = 0;
|
|
||||||
count -= s;
|
|
||||||
nr_pages--;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (index-start)
|
|
||||||
sg_mark_end(&sg[index - 1]);
|
|
||||||
return index - start;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* p9_virtio_request - issue a request
|
|
||||||
* @client: client instance issuing the request
|
|
||||||
* @req: request to be issued
|
|
||||||
*
|
|
||||||
*/
|
|
||||||
|
|
||||||
static int
|
|
||||||
p9_virtio_request(struct p9_client *client, struct p9_req_t *req)
|
|
||||||
{
|
|
||||||
int err;
|
|
||||||
int in, out, out_sgs, in_sgs;
|
|
||||||
unsigned long flags;
|
|
||||||
struct virtio_chan *chan = client->trans;
|
|
||||||
struct scatterlist *sgs[2];
|
|
||||||
|
|
||||||
p9_debug(P9_DEBUG_TRANS, "9p debug: virtio request\n");
|
|
||||||
|
|
||||||
req->status = REQ_STATUS_SENT;
|
|
||||||
req_retry:
|
|
||||||
spin_lock_irqsave(&chan->lock, flags);
|
|
||||||
|
|
||||||
out_sgs = in_sgs = 0;
|
|
||||||
/* Handle out VirtIO ring buffers */
|
|
||||||
out = pack_sg_list(chan->sg, 0,
|
|
||||||
VIRTQUEUE_NUM, req->tc->sdata, req->tc->size);
|
|
||||||
if (out)
|
|
||||||
sgs[out_sgs++] = chan->sg;
|
|
||||||
|
|
||||||
in = pack_sg_list(chan->sg, out,
|
|
||||||
VIRTQUEUE_NUM, req->rc->sdata, req->rc->capacity);
|
|
||||||
if (in)
|
|
||||||
sgs[out_sgs + in_sgs++] = chan->sg + out;
|
|
||||||
|
|
||||||
err = virtqueue_add_sgs(chan->vq, sgs, out_sgs, in_sgs, req->tc,
|
|
||||||
GFP_ATOMIC);
|
|
||||||
if (err < 0) {
|
|
||||||
if (err == -ENOSPC) {
|
|
||||||
chan->ring_bufs_avail = 0;
|
|
||||||
spin_unlock_irqrestore(&chan->lock, flags);
|
|
||||||
err = wait_event_killable(*chan->vc_wq,
|
|
||||||
chan->ring_bufs_avail);
|
|
||||||
if (err == -ERESTARTSYS)
|
|
||||||
return err;
|
|
||||||
|
|
||||||
p9_debug(P9_DEBUG_TRANS, "Retry virtio request\n");
|
|
||||||
goto req_retry;
|
|
||||||
} else {
|
|
||||||
spin_unlock_irqrestore(&chan->lock, flags);
|
|
||||||
p9_debug(P9_DEBUG_TRANS,
|
|
||||||
"virtio rpc add_sgs returned failure\n");
|
|
||||||
return -EIO;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
virtqueue_kick(chan->vq);
|
|
||||||
spin_unlock_irqrestore(&chan->lock, flags);
|
|
||||||
|
|
||||||
p9_debug(P9_DEBUG_TRANS, "virtio request kicked\n");
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
static int p9_get_mapped_pages(struct virtio_chan *chan,
|
|
||||||
struct page ***pages,
|
|
||||||
struct iov_iter *data,
|
|
||||||
int count,
|
|
||||||
size_t *offs,
|
|
||||||
int *need_drop)
|
|
||||||
{
|
|
||||||
int nr_pages;
|
|
||||||
int err;
|
|
||||||
|
|
||||||
if (!iov_iter_count(data))
|
|
||||||
return 0;
|
|
||||||
|
|
||||||
if (!(data->type & ITER_KVEC)) {
|
|
||||||
int n;
|
|
||||||
/*
|
|
||||||
* We allow only p9_max_pages pinned. We wait for the
|
|
||||||
* Other zc request to finish here
|
|
||||||
*/
|
|
||||||
if (atomic_read(&vp_pinned) >= chan->p9_max_pages) {
|
|
||||||
err = wait_event_killable(vp_wq,
|
|
||||||
(atomic_read(&vp_pinned) < chan->p9_max_pages));
|
|
||||||
if (err == -ERESTARTSYS)
|
|
||||||
return err;
|
|
||||||
}
|
|
||||||
n = iov_iter_get_pages_alloc(data, pages, count, offs);
|
|
||||||
if (n < 0)
|
|
||||||
return n;
|
|
||||||
*need_drop = 1;
|
|
||||||
nr_pages = DIV_ROUND_UP(n + *offs, PAGE_SIZE);
|
|
||||||
atomic_add(nr_pages, &vp_pinned);
|
|
||||||
return n;
|
|
||||||
} else {
|
|
||||||
/* kernel buffer, no need to pin pages */
|
|
||||||
int index;
|
|
||||||
size_t len;
|
|
||||||
void *p;
|
|
||||||
|
|
||||||
/* we'd already checked that it's non-empty */
|
|
||||||
while (1) {
|
|
||||||
len = iov_iter_single_seg_count(data);
|
|
||||||
if (likely(len)) {
|
|
||||||
p = data->kvec->iov_base + data->iov_offset;
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
iov_iter_advance(data, 0);
|
|
||||||
}
|
|
||||||
if (len > count)
|
|
||||||
len = count;
|
|
||||||
|
|
||||||
nr_pages = DIV_ROUND_UP((unsigned long)p + len, PAGE_SIZE) -
|
|
||||||
(unsigned long)p / PAGE_SIZE;
|
|
||||||
|
|
||||||
*pages = kmalloc(sizeof(struct page *) * nr_pages, GFP_NOFS);
|
|
||||||
if (!*pages)
|
|
||||||
return -ENOMEM;
|
|
||||||
|
|
||||||
*need_drop = 0;
|
|
||||||
p -= (*offs = (unsigned long)p % PAGE_SIZE);
|
|
||||||
for (index = 0; index < nr_pages; index++) {
|
|
||||||
if (is_vmalloc_addr(p))
|
|
||||||
(*pages)[index] = vmalloc_to_page(p);
|
|
||||||
else
|
|
||||||
(*pages)[index] = kmap_to_page(p);
|
|
||||||
p += PAGE_SIZE;
|
|
||||||
}
|
|
||||||
return len;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* p9_virtio_zc_request - issue a zero copy request
|
|
||||||
* @client: client instance issuing the request
|
|
||||||
* @req: request to be issued
|
|
||||||
* @uidata: user bffer that should be ued for zero copy read
|
|
||||||
* @uodata: user buffer that shoud be user for zero copy write
|
|
||||||
* @inlen: read buffer size
|
|
||||||
* @olen: write buffer size
|
|
||||||
* @hdrlen: reader header size, This is the size of response protocol data
|
|
||||||
*
|
|
||||||
*/
|
|
||||||
static int
|
|
||||||
p9_virtio_zc_request(struct p9_client *client, struct p9_req_t *req,
|
|
||||||
struct iov_iter *uidata, struct iov_iter *uodata,
|
|
||||||
int inlen, int outlen, int in_hdr_len)
|
|
||||||
{
|
|
||||||
int in, out, err, out_sgs, in_sgs;
|
|
||||||
unsigned long flags;
|
|
||||||
int in_nr_pages = 0, out_nr_pages = 0;
|
|
||||||
struct page **in_pages = NULL, **out_pages = NULL;
|
|
||||||
struct virtio_chan *chan = client->trans;
|
|
||||||
struct scatterlist *sgs[4];
|
|
||||||
size_t offs;
|
|
||||||
int need_drop = 0;
|
|
||||||
|
|
||||||
p9_debug(P9_DEBUG_TRANS, "virtio request\n");
|
|
||||||
|
|
||||||
if (uodata) {
|
|
||||||
__le32 sz;
|
|
||||||
int n = p9_get_mapped_pages(chan, &out_pages, uodata,
|
|
||||||
outlen, &offs, &need_drop);
|
|
||||||
if (n < 0)
|
|
||||||
return n;
|
|
||||||
out_nr_pages = DIV_ROUND_UP(n + offs, PAGE_SIZE);
|
|
||||||
if (n != outlen) {
|
|
||||||
__le32 v = cpu_to_le32(n);
|
|
||||||
memcpy(&req->tc->sdata[req->tc->size - 4], &v, 4);
|
|
||||||
outlen = n;
|
|
||||||
}
|
|
||||||
/* The size field of the message must include the length of the
|
|
||||||
* header and the length of the data. We didn't actually know
|
|
||||||
* the length of the data until this point so add it in now.
|
|
||||||
*/
|
|
||||||
sz = cpu_to_le32(req->tc->size + outlen);
|
|
||||||
memcpy(&req->tc->sdata[0], &sz, sizeof(sz));
|
|
||||||
} else if (uidata) {
|
|
||||||
int n = p9_get_mapped_pages(chan, &in_pages, uidata,
|
|
||||||
inlen, &offs, &need_drop);
|
|
||||||
if (n < 0)
|
|
||||||
return n;
|
|
||||||
in_nr_pages = DIV_ROUND_UP(n + offs, PAGE_SIZE);
|
|
||||||
if (n != inlen) {
|
|
||||||
__le32 v = cpu_to_le32(n);
|
|
||||||
memcpy(&req->tc->sdata[req->tc->size - 4], &v, 4);
|
|
||||||
inlen = n;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
req->status = REQ_STATUS_SENT;
|
|
||||||
req_retry_pinned:
|
|
||||||
spin_lock_irqsave(&chan->lock, flags);
|
|
||||||
|
|
||||||
out_sgs = in_sgs = 0;
|
|
||||||
|
|
||||||
/* out data */
|
|
||||||
out = pack_sg_list(chan->sg, 0,
|
|
||||||
VIRTQUEUE_NUM, req->tc->sdata, req->tc->size);
|
|
||||||
|
|
||||||
if (out)
|
|
||||||
sgs[out_sgs++] = chan->sg;
|
|
||||||
|
|
||||||
if (out_pages) {
|
|
||||||
sgs[out_sgs++] = chan->sg + out;
|
|
||||||
out += pack_sg_list_p(chan->sg, out, VIRTQUEUE_NUM,
|
|
||||||
out_pages, out_nr_pages, offs, outlen);
|
|
||||||
}
|
|
||||||
|
|
||||||
/*
|
|
||||||
* Take care of in data
|
|
||||||
* For example TREAD have 11.
|
|
||||||
* 11 is the read/write header = PDU Header(7) + IO Size (4).
|
|
||||||
* Arrange in such a way that server places header in the
|
|
||||||
* alloced memory and payload onto the user buffer.
|
|
||||||
*/
|
|
||||||
in = pack_sg_list(chan->sg, out,
|
|
||||||
VIRTQUEUE_NUM, req->rc->sdata, in_hdr_len);
|
|
||||||
if (in)
|
|
||||||
sgs[out_sgs + in_sgs++] = chan->sg + out;
|
|
||||||
|
|
||||||
if (in_pages) {
|
|
||||||
sgs[out_sgs + in_sgs++] = chan->sg + out + in;
|
|
||||||
in += pack_sg_list_p(chan->sg, out + in, VIRTQUEUE_NUM,
|
|
||||||
in_pages, in_nr_pages, offs, inlen);
|
|
||||||
}
|
|
||||||
|
|
||||||
BUG_ON(out_sgs + in_sgs > ARRAY_SIZE(sgs));
|
|
||||||
err = virtqueue_add_sgs(chan->vq, sgs, out_sgs, in_sgs, req->tc,
|
|
||||||
GFP_ATOMIC);
|
|
||||||
if (err < 0) {
|
|
||||||
if (err == -ENOSPC) {
|
|
||||||
chan->ring_bufs_avail = 0;
|
|
||||||
spin_unlock_irqrestore(&chan->lock, flags);
|
|
||||||
err = wait_event_killable(*chan->vc_wq,
|
|
||||||
chan->ring_bufs_avail);
|
|
||||||
if (err == -ERESTARTSYS)
|
|
||||||
goto err_out;
|
|
||||||
|
|
||||||
p9_debug(P9_DEBUG_TRANS, "Retry virtio request\n");
|
|
||||||
goto req_retry_pinned;
|
|
||||||
} else {
|
|
||||||
spin_unlock_irqrestore(&chan->lock, flags);
|
|
||||||
p9_debug(P9_DEBUG_TRANS,
|
|
||||||
"virtio rpc add_sgs returned failure\n");
|
|
||||||
err = -EIO;
|
|
||||||
goto err_out;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
virtqueue_kick(chan->vq);
|
|
||||||
spin_unlock_irqrestore(&chan->lock, flags);
|
|
||||||
p9_debug(P9_DEBUG_TRANS, "virtio request kicked\n");
|
|
||||||
err = wait_event_killable(*req->wq, req->status >= REQ_STATUS_RCVD);
|
|
||||||
/*
|
|
||||||
* Non kernel buffers are pinned, unpin them
|
|
||||||
*/
|
|
||||||
err_out:
|
|
||||||
if (need_drop) {
|
|
||||||
if (in_pages) {
|
|
||||||
p9_release_pages(in_pages, in_nr_pages);
|
|
||||||
atomic_sub(in_nr_pages, &vp_pinned);
|
|
||||||
}
|
|
||||||
if (out_pages) {
|
|
||||||
p9_release_pages(out_pages, out_nr_pages);
|
|
||||||
atomic_sub(out_nr_pages, &vp_pinned);
|
|
||||||
}
|
|
||||||
/* wakeup anybody waiting for slots to pin pages */
|
|
||||||
wake_up(&vp_wq);
|
|
||||||
}
|
|
||||||
kfree(in_pages);
|
|
||||||
kfree(out_pages);
|
|
||||||
return err;
|
|
||||||
}
|
|
||||||
|
|
||||||
static ssize_t p9_mount_tag_show(struct device *dev,
|
|
||||||
struct device_attribute *attr, char *buf)
|
|
||||||
{
|
|
||||||
struct virtio_chan *chan;
|
|
||||||
struct virtio_device *vdev;
|
|
||||||
|
|
||||||
vdev = dev_to_virtio(dev);
|
|
||||||
chan = vdev->priv;
|
|
||||||
|
|
||||||
memcpy(buf, chan->tag, chan->tag_len);
|
|
||||||
buf[chan->tag_len] = 0;
|
|
||||||
|
|
||||||
return chan->tag_len + 1;
|
|
||||||
}
|
|
||||||
|
|
||||||
static DEVICE_ATTR(mount_tag, 0444, p9_mount_tag_show, NULL);
|
|
||||||
|
|
||||||
/**
|
|
||||||
* p9_virtio_probe - probe for existence of 9P virtio channels
|
|
||||||
* @vdev: virtio device to probe
|
|
||||||
*
|
|
||||||
* This probes for existing virtio channels.
|
|
||||||
*
|
|
||||||
*/
|
|
||||||
|
|
||||||
static int p9_virtio_probe(struct virtio_device *vdev)
|
|
||||||
{
|
|
||||||
__u16 tag_len;
|
|
||||||
char *tag;
|
|
||||||
int err;
|
|
||||||
struct virtio_chan *chan;
|
|
||||||
|
|
||||||
if (!vdev->config->get) {
|
|
||||||
dev_err(&vdev->dev, "%s failure: config access disabled\n",
|
|
||||||
__func__);
|
|
||||||
return -EINVAL;
|
|
||||||
}
|
|
||||||
|
|
||||||
chan = kmalloc(sizeof(struct virtio_chan), GFP_KERNEL);
|
|
||||||
if (!chan) {
|
|
||||||
pr_err("Failed to allocate virtio 9P channel\n");
|
|
||||||
err = -ENOMEM;
|
|
||||||
goto fail;
|
|
||||||
}
|
|
||||||
|
|
||||||
chan->vdev = vdev;
|
|
||||||
|
|
||||||
/* We expect one virtqueue, for requests. */
|
|
||||||
chan->vq = virtio_find_single_vq(vdev, req_done, "requests");
|
|
||||||
if (IS_ERR(chan->vq)) {
|
|
||||||
err = PTR_ERR(chan->vq);
|
|
||||||
goto out_free_chan;
|
|
||||||
}
|
|
||||||
chan->vq->vdev->priv = chan;
|
|
||||||
spin_lock_init(&chan->lock);
|
|
||||||
|
|
||||||
sg_init_table(chan->sg, VIRTQUEUE_NUM);
|
|
||||||
|
|
||||||
chan->inuse = false;
|
|
||||||
if (virtio_has_feature(vdev, VIRTIO_9P_MOUNT_TAG)) {
|
|
||||||
virtio_cread(vdev, struct virtio_9p_config, tag_len, &tag_len);
|
|
||||||
} else {
|
|
||||||
err = -EINVAL;
|
|
||||||
goto out_free_vq;
|
|
||||||
}
|
|
||||||
tag = kmalloc(tag_len, GFP_KERNEL);
|
|
||||||
if (!tag) {
|
|
||||||
err = -ENOMEM;
|
|
||||||
goto out_free_vq;
|
|
||||||
}
|
|
||||||
|
|
||||||
virtio_cread_bytes(vdev, offsetof(struct virtio_9p_config, tag),
|
|
||||||
tag, tag_len);
|
|
||||||
chan->tag = tag;
|
|
||||||
chan->tag_len = tag_len;
|
|
||||||
err = sysfs_create_file(&(vdev->dev.kobj), &dev_attr_mount_tag.attr);
|
|
||||||
if (err) {
|
|
||||||
goto out_free_tag;
|
|
||||||
}
|
|
||||||
chan->vc_wq = kmalloc(sizeof(wait_queue_head_t), GFP_KERNEL);
|
|
||||||
if (!chan->vc_wq) {
|
|
||||||
err = -ENOMEM;
|
|
||||||
goto out_free_tag;
|
|
||||||
}
|
|
||||||
init_waitqueue_head(chan->vc_wq);
|
|
||||||
chan->ring_bufs_avail = 1;
|
|
||||||
/* Ceiling limit to avoid denial of service attacks */
|
|
||||||
chan->p9_max_pages = nr_free_buffer_pages()/4;
|
|
||||||
|
|
||||||
virtio_device_ready(vdev);
|
|
||||||
|
|
||||||
mutex_lock(&virtio_9p_lock);
|
|
||||||
list_add_tail(&chan->chan_list, &virtio_chan_list);
|
|
||||||
mutex_unlock(&virtio_9p_lock);
|
|
||||||
|
|
||||||
/* Let udev rules use the new mount_tag attribute. */
|
|
||||||
kobject_uevent(&(vdev->dev.kobj), KOBJ_CHANGE);
|
|
||||||
|
|
||||||
return 0;
|
|
||||||
|
|
||||||
out_free_tag:
|
|
||||||
kfree(tag);
|
|
||||||
out_free_vq:
|
|
||||||
vdev->config->del_vqs(vdev);
|
|
||||||
out_free_chan:
|
|
||||||
kfree(chan);
|
|
||||||
fail:
|
|
||||||
return err;
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
/**
|
|
||||||
* p9_virtio_create - allocate a new virtio channel
|
|
||||||
* @client: client instance invoking this transport
|
|
||||||
* @devname: string identifying the channel to connect to (unused)
|
|
||||||
* @args: args passed from sys_mount() for per-transport options (unused)
|
|
||||||
*
|
|
||||||
* This sets up a transport channel for 9p communication. Right now
|
|
||||||
* we only match the first available channel, but eventually we couldlook up
|
|
||||||
* alternate channels by matching devname versus a virtio_config entry.
|
|
||||||
* We use a simple reference count mechanism to ensure that only a single
|
|
||||||
* mount has a channel open at a time.
|
|
||||||
*
|
|
||||||
*/
|
|
||||||
|
|
||||||
static int
|
|
||||||
p9_virtio_create(struct p9_client *client, const char *devname, char *args)
|
|
||||||
{
|
|
||||||
struct virtio_chan *chan;
|
|
||||||
int ret = -ENOENT;
|
|
||||||
int found = 0;
|
|
||||||
|
|
||||||
if (devname == NULL)
|
|
||||||
return -EINVAL;
|
|
||||||
|
|
||||||
mutex_lock(&virtio_9p_lock);
|
|
||||||
list_for_each_entry(chan, &virtio_chan_list, chan_list) {
|
|
||||||
if (!strncmp(devname, chan->tag, chan->tag_len) &&
|
|
||||||
strlen(devname) == chan->tag_len) {
|
|
||||||
if (!chan->inuse) {
|
|
||||||
chan->inuse = true;
|
|
||||||
found = 1;
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
ret = -EBUSY;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
mutex_unlock(&virtio_9p_lock);
|
|
||||||
|
|
||||||
if (!found) {
|
|
||||||
pr_err("no channels available\n");
|
|
||||||
return ret;
|
|
||||||
}
|
|
||||||
|
|
||||||
client->trans = (void *)chan;
|
|
||||||
client->status = Connected;
|
|
||||||
chan->client = client;
|
|
||||||
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* p9_virtio_remove - clean up resources associated with a virtio device
|
|
||||||
* @vdev: virtio device to remove
|
|
||||||
*
|
|
||||||
*/
|
|
||||||
|
|
||||||
static void p9_virtio_remove(struct virtio_device *vdev)
|
|
||||||
{
|
|
||||||
struct virtio_chan *chan = vdev->priv;
|
|
||||||
unsigned long warning_time;
|
|
||||||
|
|
||||||
mutex_lock(&virtio_9p_lock);
|
|
||||||
|
|
||||||
/* Remove self from list so we don't get new users. */
|
|
||||||
list_del(&chan->chan_list);
|
|
||||||
warning_time = jiffies;
|
|
||||||
|
|
||||||
/* Wait for existing users to close. */
|
|
||||||
while (chan->inuse) {
|
|
||||||
mutex_unlock(&virtio_9p_lock);
|
|
||||||
msleep(250);
|
|
||||||
if (time_after(jiffies, warning_time + 10 * HZ)) {
|
|
||||||
dev_emerg(&vdev->dev,
|
|
||||||
"p9_virtio_remove: waiting for device in use.\n");
|
|
||||||
warning_time = jiffies;
|
|
||||||
}
|
|
||||||
mutex_lock(&virtio_9p_lock);
|
|
||||||
}
|
|
||||||
|
|
||||||
mutex_unlock(&virtio_9p_lock);
|
|
||||||
|
|
||||||
vdev->config->reset(vdev);
|
|
||||||
vdev->config->del_vqs(vdev);
|
|
||||||
|
|
||||||
sysfs_remove_file(&(vdev->dev.kobj), &dev_attr_mount_tag.attr);
|
|
||||||
kobject_uevent(&(vdev->dev.kobj), KOBJ_CHANGE);
|
|
||||||
kfree(chan->tag);
|
|
||||||
kfree(chan->vc_wq);
|
|
||||||
kfree(chan);
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
static struct virtio_device_id id_table[] = {
|
|
||||||
{ VIRTIO_ID_9P, VIRTIO_DEV_ANY_ID },
|
|
||||||
{ 0 },
|
|
||||||
};
|
|
||||||
|
|
||||||
static unsigned int features[] = {
|
|
||||||
VIRTIO_9P_MOUNT_TAG,
|
|
||||||
};
|
|
||||||
|
|
||||||
/* The standard "struct lguest_driver": */
|
|
||||||
static struct virtio_driver p9_virtio_drv = {
|
|
||||||
.feature_table = features,
|
|
||||||
.feature_table_size = ARRAY_SIZE(features),
|
|
||||||
.driver.name = KBUILD_MODNAME,
|
|
||||||
.driver.owner = THIS_MODULE,
|
|
||||||
.id_table = id_table,
|
|
||||||
.probe = p9_virtio_probe,
|
|
||||||
.remove = p9_virtio_remove,
|
|
||||||
};
|
|
||||||
|
|
||||||
static struct p9_trans_module p9_virtio_trans = {
|
|
||||||
.name = "virtio",
|
|
||||||
.create = p9_virtio_create,
|
|
||||||
.close = p9_virtio_close,
|
|
||||||
.request = p9_virtio_request,
|
|
||||||
.zc_request = p9_virtio_zc_request,
|
|
||||||
.cancel = p9_virtio_cancel,
|
|
||||||
/*
|
|
||||||
* We leave one entry for input and one entry for response
|
|
||||||
* headers. We also skip one more entry to accomodate, address
|
|
||||||
* that are not at page boundary, that can result in an extra
|
|
||||||
* page in zero copy.
|
|
||||||
*/
|
|
||||||
.maxsize = PAGE_SIZE * (VIRTQUEUE_NUM - 3),
|
|
||||||
.def = 1,
|
|
||||||
.owner = THIS_MODULE,
|
|
||||||
};
|
|
||||||
|
|
||||||
/* The standard init function */
|
|
||||||
static int __init p9_virtio_init(void)
|
|
||||||
{
|
|
||||||
INIT_LIST_HEAD(&virtio_chan_list);
|
|
||||||
|
|
||||||
v9fs_register_trans(&p9_virtio_trans);
|
|
||||||
return register_virtio_driver(&p9_virtio_drv);
|
|
||||||
}
|
|
||||||
|
|
||||||
static void __exit p9_virtio_cleanup(void)
|
|
||||||
{
|
|
||||||
unregister_virtio_driver(&p9_virtio_drv);
|
|
||||||
v9fs_unregister_trans(&p9_virtio_trans);
|
|
||||||
}
|
|
||||||
|
|
||||||
module_init(p9_virtio_init);
|
|
||||||
module_exit(p9_virtio_cleanup);
|
|
||||||
|
|
||||||
MODULE_DEVICE_TABLE(virtio, id_table);
|
|
||||||
MODULE_AUTHOR("Eric Van Hensbergen <ericvh@gmail.com>");
|
|
||||||
MODULE_DESCRIPTION("Virtio 9p Transport");
|
|
||||||
MODULE_LICENSE("GPL");
|
|
@ -1,141 +0,0 @@
|
|||||||
/*
|
|
||||||
* net/9p/util.c
|
|
||||||
*
|
|
||||||
* This file contains some helper functions
|
|
||||||
*
|
|
||||||
* Copyright (C) 2007 by Latchesar Ionkov <lucho@ionkov.net>
|
|
||||||
* Copyright (C) 2004 by Eric Van Hensbergen <ericvh@gmail.com>
|
|
||||||
* Copyright (C) 2002 by Ron Minnich <rminnich@lanl.gov>
|
|
||||||
*
|
|
||||||
* This program is free software; you can redistribute it and/or modify
|
|
||||||
* it under the terms of the GNU General Public License version 2
|
|
||||||
* as published by the Free Software Foundation.
|
|
||||||
*
|
|
||||||
* This program is distributed in the hope that it will be useful,
|
|
||||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
||||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
||||||
* GNU General Public License for more details.
|
|
||||||
*
|
|
||||||
* You should have received a copy of the GNU General Public License
|
|
||||||
* along with this program; if not, write to:
|
|
||||||
* Free Software Foundation
|
|
||||||
* 51 Franklin Street, Fifth Floor
|
|
||||||
* Boston, MA 02111-1301 USA
|
|
||||||
*
|
|
||||||
*/
|
|
||||||
|
|
||||||
#include <linux/module.h>
|
|
||||||
#include <linux/errno.h>
|
|
||||||
#include <linux/fs.h>
|
|
||||||
#include <linux/sched.h>
|
|
||||||
#include <linux/parser.h>
|
|
||||||
#include <linux/idr.h>
|
|
||||||
#include <linux/slab.h>
|
|
||||||
#include <net/9p/9p.h>
|
|
||||||
|
|
||||||
/**
|
|
||||||
* struct p9_idpool - per-connection accounting for tag idpool
|
|
||||||
* @lock: protects the pool
|
|
||||||
* @pool: idr to allocate tag id from
|
|
||||||
*
|
|
||||||
*/
|
|
||||||
|
|
||||||
struct p9_idpool {
|
|
||||||
spinlock_t lock;
|
|
||||||
struct idr pool;
|
|
||||||
};
|
|
||||||
|
|
||||||
/**
|
|
||||||
* p9_idpool_create - create a new per-connection id pool
|
|
||||||
*
|
|
||||||
*/
|
|
||||||
|
|
||||||
struct p9_idpool *p9_idpool_create(void)
|
|
||||||
{
|
|
||||||
struct p9_idpool *p;
|
|
||||||
|
|
||||||
p = kmalloc(sizeof(struct p9_idpool), GFP_KERNEL);
|
|
||||||
if (!p)
|
|
||||||
return ERR_PTR(-ENOMEM);
|
|
||||||
|
|
||||||
spin_lock_init(&p->lock);
|
|
||||||
idr_init(&p->pool);
|
|
||||||
|
|
||||||
return p;
|
|
||||||
}
|
|
||||||
EXPORT_SYMBOL(p9_idpool_create);
|
|
||||||
|
|
||||||
/**
|
|
||||||
* p9_idpool_destroy - create a new per-connection id pool
|
|
||||||
* @p: idpool to destroy
|
|
||||||
*/
|
|
||||||
|
|
||||||
void p9_idpool_destroy(struct p9_idpool *p)
|
|
||||||
{
|
|
||||||
idr_destroy(&p->pool);
|
|
||||||
kfree(p);
|
|
||||||
}
|
|
||||||
EXPORT_SYMBOL(p9_idpool_destroy);
|
|
||||||
|
|
||||||
/**
|
|
||||||
* p9_idpool_get - allocate numeric id from pool
|
|
||||||
* @p: pool to allocate from
|
|
||||||
*
|
|
||||||
* Bugs: This seems to be an awful generic function, should it be in idr.c with
|
|
||||||
* the lock included in struct idr?
|
|
||||||
*/
|
|
||||||
|
|
||||||
int p9_idpool_get(struct p9_idpool *p)
|
|
||||||
{
|
|
||||||
int i;
|
|
||||||
unsigned long flags;
|
|
||||||
|
|
||||||
idr_preload(GFP_NOFS);
|
|
||||||
spin_lock_irqsave(&p->lock, flags);
|
|
||||||
|
|
||||||
/* no need to store exactly p, we just need something non-null */
|
|
||||||
i = idr_alloc(&p->pool, p, 0, 0, GFP_NOWAIT);
|
|
||||||
|
|
||||||
spin_unlock_irqrestore(&p->lock, flags);
|
|
||||||
idr_preload_end();
|
|
||||||
if (i < 0)
|
|
||||||
return -1;
|
|
||||||
|
|
||||||
p9_debug(P9_DEBUG_MUX, " id %d pool %p\n", i, p);
|
|
||||||
return i;
|
|
||||||
}
|
|
||||||
EXPORT_SYMBOL(p9_idpool_get);
|
|
||||||
|
|
||||||
/**
|
|
||||||
* p9_idpool_put - release numeric id from pool
|
|
||||||
* @id: numeric id which is being released
|
|
||||||
* @p: pool to release id into
|
|
||||||
*
|
|
||||||
* Bugs: This seems to be an awful generic function, should it be in idr.c with
|
|
||||||
* the lock included in struct idr?
|
|
||||||
*/
|
|
||||||
|
|
||||||
void p9_idpool_put(int id, struct p9_idpool *p)
|
|
||||||
{
|
|
||||||
unsigned long flags;
|
|
||||||
|
|
||||||
p9_debug(P9_DEBUG_MUX, " id %d pool %p\n", id, p);
|
|
||||||
|
|
||||||
spin_lock_irqsave(&p->lock, flags);
|
|
||||||
idr_remove(&p->pool, id);
|
|
||||||
spin_unlock_irqrestore(&p->lock, flags);
|
|
||||||
}
|
|
||||||
EXPORT_SYMBOL(p9_idpool_put);
|
|
||||||
|
|
||||||
/**
|
|
||||||
* p9_idpool_check - check if the specified id is available
|
|
||||||
* @id: id to check
|
|
||||||
* @p: pool to check
|
|
||||||
*/
|
|
||||||
|
|
||||||
int p9_idpool_check(int id, struct p9_idpool *p)
|
|
||||||
{
|
|
||||||
return idr_find(&p->pool, id) != NULL;
|
|
||||||
}
|
|
||||||
EXPORT_SYMBOL(p9_idpool_check);
|
|
||||||
|
|
@ -1,700 +0,0 @@
|
|||||||
/*
|
|
||||||
* linux/fs/9p/v9fs.c
|
|
||||||
*
|
|
||||||
* This file contains functions assisting in mapping VFS to 9P2000
|
|
||||||
*
|
|
||||||
* Copyright (C) 2004-2008 by Eric Van Hensbergen <ericvh@gmail.com>
|
|
||||||
* Copyright (C) 2002 by Ron Minnich <rminnich@lanl.gov>
|
|
||||||
*
|
|
||||||
* This program is free software; you can redistribute it and/or modify
|
|
||||||
* it under the terms of the GNU General Public License version 2
|
|
||||||
* as published by the Free Software Foundation.
|
|
||||||
*
|
|
||||||
* This program is distributed in the hope that it will be useful,
|
|
||||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
||||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
||||||
* GNU General Public License for more details.
|
|
||||||
*
|
|
||||||
* You should have received a copy of the GNU General Public License
|
|
||||||
* along with this program; if not, write to:
|
|
||||||
* Free Software Foundation
|
|
||||||
* 51 Franklin Street, Fifth Floor
|
|
||||||
* Boston, MA 02111-1301 USA
|
|
||||||
*
|
|
||||||
*/
|
|
||||||
|
|
||||||
#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
|
|
||||||
|
|
||||||
#include <linux/module.h>
|
|
||||||
#include <linux/errno.h>
|
|
||||||
#include <linux/fs.h>
|
|
||||||
#include <linux/sched.h>
|
|
||||||
#include <linux/parser.h>
|
|
||||||
#include <linux/idr.h>
|
|
||||||
#include <linux/slab.h>
|
|
||||||
#include <net/9p/9p.h>
|
|
||||||
#include <net/9p/client.h>
|
|
||||||
#include <net/9p/transport.h>
|
|
||||||
#include "v9fs.h"
|
|
||||||
#include "v9fs_vfs.h"
|
|
||||||
#include "cache.h"
|
|
||||||
|
|
||||||
static DEFINE_SPINLOCK(v9fs_sessionlist_lock);
|
|
||||||
static LIST_HEAD(v9fs_sessionlist);
|
|
||||||
struct kmem_cache *v9fs_inode_cache;
|
|
||||||
|
|
||||||
/*
|
|
||||||
* Option Parsing (code inspired by NFS code)
|
|
||||||
* NOTE: each transport will parse its own options
|
|
||||||
*/
|
|
||||||
|
|
||||||
enum {
|
|
||||||
/* Options that take integer arguments */
|
|
||||||
Opt_debug, Opt_dfltuid, Opt_dfltgid, Opt_afid,
|
|
||||||
/* String options */
|
|
||||||
Opt_uname, Opt_remotename, Opt_cache, Opt_cachetag,
|
|
||||||
/* Options that take no arguments */
|
|
||||||
Opt_nodevmap,
|
|
||||||
/* Cache options */
|
|
||||||
Opt_cache_loose, Opt_fscache, Opt_mmap,
|
|
||||||
/* Access options */
|
|
||||||
Opt_access, Opt_posixacl,
|
|
||||||
/* Lock timeout option */
|
|
||||||
Opt_locktimeout,
|
|
||||||
/* Error token */
|
|
||||||
Opt_err
|
|
||||||
};
|
|
||||||
|
|
||||||
static const match_table_t tokens = {
|
|
||||||
{Opt_debug, "debug=%x"},
|
|
||||||
{Opt_dfltuid, "dfltuid=%u"},
|
|
||||||
{Opt_dfltgid, "dfltgid=%u"},
|
|
||||||
{Opt_afid, "afid=%u"},
|
|
||||||
{Opt_uname, "uname=%s"},
|
|
||||||
{Opt_remotename, "aname=%s"},
|
|
||||||
{Opt_nodevmap, "nodevmap"},
|
|
||||||
{Opt_cache, "cache=%s"},
|
|
||||||
{Opt_cache_loose, "loose"},
|
|
||||||
{Opt_fscache, "fscache"},
|
|
||||||
{Opt_mmap, "mmap"},
|
|
||||||
{Opt_cachetag, "cachetag=%s"},
|
|
||||||
{Opt_access, "access=%s"},
|
|
||||||
{Opt_posixacl, "posixacl"},
|
|
||||||
{Opt_locktimeout, "locktimeout=%u"},
|
|
||||||
{Opt_err, NULL}
|
|
||||||
};
|
|
||||||
|
|
||||||
/* Interpret mount options for cache mode */
|
|
||||||
static int get_cache_mode(char *s)
|
|
||||||
{
|
|
||||||
int version = -EINVAL;
|
|
||||||
|
|
||||||
if (!strcmp(s, "loose")) {
|
|
||||||
version = CACHE_LOOSE;
|
|
||||||
p9_debug(P9_DEBUG_9P, "Cache mode: loose\n");
|
|
||||||
} else if (!strcmp(s, "fscache")) {
|
|
||||||
version = CACHE_FSCACHE;
|
|
||||||
p9_debug(P9_DEBUG_9P, "Cache mode: fscache\n");
|
|
||||||
} else if (!strcmp(s, "mmap")) {
|
|
||||||
version = CACHE_MMAP;
|
|
||||||
p9_debug(P9_DEBUG_9P, "Cache mode: mmap\n");
|
|
||||||
} else if (!strcmp(s, "none")) {
|
|
||||||
version = CACHE_NONE;
|
|
||||||
p9_debug(P9_DEBUG_9P, "Cache mode: none\n");
|
|
||||||
} else
|
|
||||||
pr_info("Unknown Cache mode %s\n", s);
|
|
||||||
return version;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* v9fs_parse_options - parse mount options into session structure
|
|
||||||
* @v9ses: existing v9fs session information
|
|
||||||
*
|
|
||||||
* Return 0 upon success, -ERRNO upon failure.
|
|
||||||
*/
|
|
||||||
|
|
||||||
static int v9fs_parse_options(struct v9fs_session_info *v9ses, char *opts)
|
|
||||||
{
|
|
||||||
char *options, *tmp_options;
|
|
||||||
substring_t args[MAX_OPT_ARGS];
|
|
||||||
char *p;
|
|
||||||
int option = 0;
|
|
||||||
char *s, *e;
|
|
||||||
int ret = 0;
|
|
||||||
|
|
||||||
/* setup defaults */
|
|
||||||
v9ses->afid = ~0;
|
|
||||||
v9ses->debug = 0;
|
|
||||||
v9ses->cache = CACHE_NONE;
|
|
||||||
#ifdef CONFIG_9P_FSCACHE
|
|
||||||
v9ses->cachetag = NULL;
|
|
||||||
#endif
|
|
||||||
v9ses->session_lock_timeout = P9_LOCK_TIMEOUT;
|
|
||||||
|
|
||||||
if (!opts)
|
|
||||||
return 0;
|
|
||||||
|
|
||||||
tmp_options = kstrdup(opts, GFP_KERNEL);
|
|
||||||
if (!tmp_options) {
|
|
||||||
ret = -ENOMEM;
|
|
||||||
goto fail_option_alloc;
|
|
||||||
}
|
|
||||||
options = tmp_options;
|
|
||||||
|
|
||||||
while ((p = strsep(&options, ",")) != NULL) {
|
|
||||||
int token, r;
|
|
||||||
if (!*p)
|
|
||||||
continue;
|
|
||||||
token = match_token(p, tokens, args);
|
|
||||||
switch (token) {
|
|
||||||
case Opt_debug:
|
|
||||||
r = match_int(&args[0], &option);
|
|
||||||
if (r < 0) {
|
|
||||||
p9_debug(P9_DEBUG_ERROR,
|
|
||||||
"integer field, but no integer?\n");
|
|
||||||
ret = r;
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
v9ses->debug = option;
|
|
||||||
#ifdef CONFIG_NET_9P_DEBUG
|
|
||||||
p9_debug_level = option;
|
|
||||||
#endif
|
|
||||||
break;
|
|
||||||
|
|
||||||
case Opt_dfltuid:
|
|
||||||
r = match_int(&args[0], &option);
|
|
||||||
if (r < 0) {
|
|
||||||
p9_debug(P9_DEBUG_ERROR,
|
|
||||||
"integer field, but no integer?\n");
|
|
||||||
ret = r;
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
v9ses->dfltuid = make_kuid(current_user_ns(), option);
|
|
||||||
if (!uid_valid(v9ses->dfltuid)) {
|
|
||||||
p9_debug(P9_DEBUG_ERROR,
|
|
||||||
"uid field, but not a uid?\n");
|
|
||||||
ret = -EINVAL;
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
break;
|
|
||||||
case Opt_dfltgid:
|
|
||||||
r = match_int(&args[0], &option);
|
|
||||||
if (r < 0) {
|
|
||||||
p9_debug(P9_DEBUG_ERROR,
|
|
||||||
"integer field, but no integer?\n");
|
|
||||||
ret = r;
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
v9ses->dfltgid = make_kgid(current_user_ns(), option);
|
|
||||||
if (!gid_valid(v9ses->dfltgid)) {
|
|
||||||
p9_debug(P9_DEBUG_ERROR,
|
|
||||||
"gid field, but not a gid?\n");
|
|
||||||
ret = -EINVAL;
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
break;
|
|
||||||
case Opt_afid:
|
|
||||||
r = match_int(&args[0], &option);
|
|
||||||
if (r < 0) {
|
|
||||||
p9_debug(P9_DEBUG_ERROR,
|
|
||||||
"integer field, but no integer?\n");
|
|
||||||
ret = r;
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
v9ses->afid = option;
|
|
||||||
break;
|
|
||||||
case Opt_uname:
|
|
||||||
kfree(v9ses->uname);
|
|
||||||
v9ses->uname = match_strdup(&args[0]);
|
|
||||||
if (!v9ses->uname) {
|
|
||||||
ret = -ENOMEM;
|
|
||||||
goto free_and_return;
|
|
||||||
}
|
|
||||||
break;
|
|
||||||
case Opt_remotename:
|
|
||||||
kfree(v9ses->aname);
|
|
||||||
v9ses->aname = match_strdup(&args[0]);
|
|
||||||
if (!v9ses->aname) {
|
|
||||||
ret = -ENOMEM;
|
|
||||||
goto free_and_return;
|
|
||||||
}
|
|
||||||
break;
|
|
||||||
case Opt_nodevmap:
|
|
||||||
v9ses->nodev = 1;
|
|
||||||
break;
|
|
||||||
case Opt_cache_loose:
|
|
||||||
v9ses->cache = CACHE_LOOSE;
|
|
||||||
break;
|
|
||||||
case Opt_fscache:
|
|
||||||
v9ses->cache = CACHE_FSCACHE;
|
|
||||||
break;
|
|
||||||
case Opt_mmap:
|
|
||||||
v9ses->cache = CACHE_MMAP;
|
|
||||||
break;
|
|
||||||
case Opt_cachetag:
|
|
||||||
#ifdef CONFIG_9P_FSCACHE
|
|
||||||
v9ses->cachetag = match_strdup(&args[0]);
|
|
||||||
#endif
|
|
||||||
break;
|
|
||||||
case Opt_cache:
|
|
||||||
s = match_strdup(&args[0]);
|
|
||||||
if (!s) {
|
|
||||||
ret = -ENOMEM;
|
|
||||||
p9_debug(P9_DEBUG_ERROR,
|
|
||||||
"problem allocating copy of cache arg\n");
|
|
||||||
goto free_and_return;
|
|
||||||
}
|
|
||||||
ret = get_cache_mode(s);
|
|
||||||
if (ret == -EINVAL) {
|
|
||||||
kfree(s);
|
|
||||||
goto free_and_return;
|
|
||||||
}
|
|
||||||
|
|
||||||
v9ses->cache = ret;
|
|
||||||
kfree(s);
|
|
||||||
break;
|
|
||||||
|
|
||||||
case Opt_access:
|
|
||||||
s = match_strdup(&args[0]);
|
|
||||||
if (!s) {
|
|
||||||
ret = -ENOMEM;
|
|
||||||
p9_debug(P9_DEBUG_ERROR,
|
|
||||||
"problem allocating copy of access arg\n");
|
|
||||||
goto free_and_return;
|
|
||||||
}
|
|
||||||
|
|
||||||
v9ses->flags &= ~V9FS_ACCESS_MASK;
|
|
||||||
if (strcmp(s, "user") == 0)
|
|
||||||
v9ses->flags |= V9FS_ACCESS_USER;
|
|
||||||
else if (strcmp(s, "any") == 0)
|
|
||||||
v9ses->flags |= V9FS_ACCESS_ANY;
|
|
||||||
else if (strcmp(s, "client") == 0) {
|
|
||||||
v9ses->flags |= V9FS_ACCESS_CLIENT;
|
|
||||||
} else {
|
|
||||||
uid_t uid;
|
|
||||||
v9ses->flags |= V9FS_ACCESS_SINGLE;
|
|
||||||
uid = simple_strtoul(s, &e, 10);
|
|
||||||
if (*e != '\0') {
|
|
||||||
ret = -EINVAL;
|
|
||||||
pr_info("Unknown access argument %s\n",
|
|
||||||
s);
|
|
||||||
kfree(s);
|
|
||||||
goto free_and_return;
|
|
||||||
}
|
|
||||||
v9ses->uid = make_kuid(current_user_ns(), uid);
|
|
||||||
if (!uid_valid(v9ses->uid)) {
|
|
||||||
ret = -EINVAL;
|
|
||||||
pr_info("Uknown uid %s\n", s);
|
|
||||||
kfree(s);
|
|
||||||
goto free_and_return;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
kfree(s);
|
|
||||||
break;
|
|
||||||
|
|
||||||
case Opt_posixacl:
|
|
||||||
#ifdef CONFIG_9P_FS_POSIX_ACL
|
|
||||||
v9ses->flags |= V9FS_POSIX_ACL;
|
|
||||||
#else
|
|
||||||
p9_debug(P9_DEBUG_ERROR,
|
|
||||||
"Not defined CONFIG_9P_FS_POSIX_ACL. Ignoring posixacl option\n");
|
|
||||||
#endif
|
|
||||||
break;
|
|
||||||
|
|
||||||
case Opt_locktimeout:
|
|
||||||
r = match_int(&args[0], &option);
|
|
||||||
if (r < 0) {
|
|
||||||
p9_debug(P9_DEBUG_ERROR,
|
|
||||||
"integer field, but no integer?\n");
|
|
||||||
ret = r;
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
if (option < 1) {
|
|
||||||
p9_debug(P9_DEBUG_ERROR,
|
|
||||||
"locktimeout must be a greater than zero integer.\n");
|
|
||||||
ret = -EINVAL;
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
v9ses->session_lock_timeout = (long)option * HZ;
|
|
||||||
break;
|
|
||||||
|
|
||||||
default:
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
free_and_return:
|
|
||||||
kfree(tmp_options);
|
|
||||||
fail_option_alloc:
|
|
||||||
return ret;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* v9fs_session_init - initialize session
|
|
||||||
* @v9ses: session information structure
|
|
||||||
* @dev_name: device being mounted
|
|
||||||
* @data: options
|
|
||||||
*
|
|
||||||
*/
|
|
||||||
|
|
||||||
struct p9_fid *v9fs_session_init(struct v9fs_session_info *v9ses,
|
|
||||||
const char *dev_name, char *data)
|
|
||||||
{
|
|
||||||
struct p9_fid *fid;
|
|
||||||
int rc = -ENOMEM;
|
|
||||||
|
|
||||||
v9ses->uname = kstrdup(V9FS_DEFUSER, GFP_KERNEL);
|
|
||||||
if (!v9ses->uname)
|
|
||||||
goto err_names;
|
|
||||||
|
|
||||||
v9ses->aname = kstrdup(V9FS_DEFANAME, GFP_KERNEL);
|
|
||||||
if (!v9ses->aname)
|
|
||||||
goto err_names;
|
|
||||||
init_rwsem(&v9ses->rename_sem);
|
|
||||||
|
|
||||||
rc = bdi_setup_and_register(&v9ses->bdi, "9p");
|
|
||||||
if (rc)
|
|
||||||
goto err_names;
|
|
||||||
|
|
||||||
v9ses->uid = INVALID_UID;
|
|
||||||
v9ses->dfltuid = V9FS_DEFUID;
|
|
||||||
v9ses->dfltgid = V9FS_DEFGID;
|
|
||||||
|
|
||||||
v9ses->clnt = p9_client_create(dev_name, data);
|
|
||||||
if (IS_ERR(v9ses->clnt)) {
|
|
||||||
rc = PTR_ERR(v9ses->clnt);
|
|
||||||
p9_debug(P9_DEBUG_ERROR, "problem initializing 9p client\n");
|
|
||||||
goto err_bdi;
|
|
||||||
}
|
|
||||||
|
|
||||||
v9ses->flags = V9FS_ACCESS_USER;
|
|
||||||
|
|
||||||
if (p9_is_proto_dotl(v9ses->clnt)) {
|
|
||||||
v9ses->flags = V9FS_ACCESS_CLIENT;
|
|
||||||
v9ses->flags |= V9FS_PROTO_2000L;
|
|
||||||
} else if (p9_is_proto_dotu(v9ses->clnt)) {
|
|
||||||
v9ses->flags |= V9FS_PROTO_2000U;
|
|
||||||
}
|
|
||||||
|
|
||||||
rc = v9fs_parse_options(v9ses, data);
|
|
||||||
if (rc < 0)
|
|
||||||
goto err_clnt;
|
|
||||||
|
|
||||||
v9ses->maxdata = v9ses->clnt->msize - P9_IOHDRSZ;
|
|
||||||
|
|
||||||
if (!v9fs_proto_dotl(v9ses) &&
|
|
||||||
((v9ses->flags & V9FS_ACCESS_MASK) == V9FS_ACCESS_CLIENT)) {
|
|
||||||
/*
|
|
||||||
* We support ACCESS_CLIENT only for dotl.
|
|
||||||
* Fall back to ACCESS_USER
|
|
||||||
*/
|
|
||||||
v9ses->flags &= ~V9FS_ACCESS_MASK;
|
|
||||||
v9ses->flags |= V9FS_ACCESS_USER;
|
|
||||||
}
|
|
||||||
/*FIXME !! */
|
|
||||||
/* for legacy mode, fall back to V9FS_ACCESS_ANY */
|
|
||||||
if (!(v9fs_proto_dotu(v9ses) || v9fs_proto_dotl(v9ses)) &&
|
|
||||||
((v9ses->flags&V9FS_ACCESS_MASK) == V9FS_ACCESS_USER)) {
|
|
||||||
|
|
||||||
v9ses->flags &= ~V9FS_ACCESS_MASK;
|
|
||||||
v9ses->flags |= V9FS_ACCESS_ANY;
|
|
||||||
v9ses->uid = INVALID_UID;
|
|
||||||
}
|
|
||||||
if (!v9fs_proto_dotl(v9ses) ||
|
|
||||||
!((v9ses->flags & V9FS_ACCESS_MASK) == V9FS_ACCESS_CLIENT)) {
|
|
||||||
/*
|
|
||||||
* We support ACL checks on clinet only if the protocol is
|
|
||||||
* 9P2000.L and access is V9FS_ACCESS_CLIENT.
|
|
||||||
*/
|
|
||||||
v9ses->flags &= ~V9FS_ACL_MASK;
|
|
||||||
}
|
|
||||||
|
|
||||||
fid = p9_client_attach(v9ses->clnt, NULL, v9ses->uname, INVALID_UID,
|
|
||||||
v9ses->aname);
|
|
||||||
if (IS_ERR(fid)) {
|
|
||||||
rc = PTR_ERR(fid);
|
|
||||||
p9_debug(P9_DEBUG_ERROR, "cannot attach\n");
|
|
||||||
goto err_clnt;
|
|
||||||
}
|
|
||||||
|
|
||||||
if ((v9ses->flags & V9FS_ACCESS_MASK) == V9FS_ACCESS_SINGLE)
|
|
||||||
fid->uid = v9ses->uid;
|
|
||||||
else
|
|
||||||
fid->uid = INVALID_UID;
|
|
||||||
|
|
||||||
#ifdef CONFIG_9P_FSCACHE
|
|
||||||
/* register the session for caching */
|
|
||||||
v9fs_cache_session_get_cookie(v9ses);
|
|
||||||
#endif
|
|
||||||
spin_lock(&v9fs_sessionlist_lock);
|
|
||||||
list_add(&v9ses->slist, &v9fs_sessionlist);
|
|
||||||
spin_unlock(&v9fs_sessionlist_lock);
|
|
||||||
|
|
||||||
return fid;
|
|
||||||
|
|
||||||
err_clnt:
|
|
||||||
p9_client_destroy(v9ses->clnt);
|
|
||||||
err_bdi:
|
|
||||||
bdi_destroy(&v9ses->bdi);
|
|
||||||
err_names:
|
|
||||||
kfree(v9ses->uname);
|
|
||||||
kfree(v9ses->aname);
|
|
||||||
return ERR_PTR(rc);
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* v9fs_session_close - shutdown a session
|
|
||||||
* @v9ses: session information structure
|
|
||||||
*
|
|
||||||
*/
|
|
||||||
|
|
||||||
void v9fs_session_close(struct v9fs_session_info *v9ses)
|
|
||||||
{
|
|
||||||
if (v9ses->clnt) {
|
|
||||||
p9_client_destroy(v9ses->clnt);
|
|
||||||
v9ses->clnt = NULL;
|
|
||||||
}
|
|
||||||
|
|
||||||
#ifdef CONFIG_9P_FSCACHE
|
|
||||||
if (v9ses->fscache) {
|
|
||||||
v9fs_cache_session_put_cookie(v9ses);
|
|
||||||
kfree(v9ses->cachetag);
|
|
||||||
}
|
|
||||||
#endif
|
|
||||||
kfree(v9ses->uname);
|
|
||||||
kfree(v9ses->aname);
|
|
||||||
|
|
||||||
bdi_destroy(&v9ses->bdi);
|
|
||||||
|
|
||||||
spin_lock(&v9fs_sessionlist_lock);
|
|
||||||
list_del(&v9ses->slist);
|
|
||||||
spin_unlock(&v9fs_sessionlist_lock);
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* v9fs_session_cancel - terminate a session
|
|
||||||
* @v9ses: session to terminate
|
|
||||||
*
|
|
||||||
* mark transport as disconnected and cancel all pending requests.
|
|
||||||
*/
|
|
||||||
|
|
||||||
void v9fs_session_cancel(struct v9fs_session_info *v9ses) {
|
|
||||||
p9_debug(P9_DEBUG_ERROR, "cancel session %p\n", v9ses);
|
|
||||||
p9_client_disconnect(v9ses->clnt);
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* v9fs_session_begin_cancel - Begin terminate of a session
|
|
||||||
* @v9ses: session to terminate
|
|
||||||
*
|
|
||||||
* After this call we don't allow any request other than clunk.
|
|
||||||
*/
|
|
||||||
|
|
||||||
void v9fs_session_begin_cancel(struct v9fs_session_info *v9ses)
|
|
||||||
{
|
|
||||||
p9_debug(P9_DEBUG_ERROR, "begin cancel session %p\n", v9ses);
|
|
||||||
p9_client_begin_disconnect(v9ses->clnt);
|
|
||||||
}
|
|
||||||
|
|
||||||
extern int v9fs_error_init(void);
|
|
||||||
|
|
||||||
static struct kobject *v9fs_kobj;
|
|
||||||
|
|
||||||
#ifdef CONFIG_9P_FSCACHE
|
|
||||||
/**
|
|
||||||
* caches_show - list caches associated with a session
|
|
||||||
*
|
|
||||||
* Returns the size of buffer written.
|
|
||||||
*/
|
|
||||||
|
|
||||||
static ssize_t caches_show(struct kobject *kobj,
|
|
||||||
struct kobj_attribute *attr,
|
|
||||||
char *buf)
|
|
||||||
{
|
|
||||||
ssize_t n = 0, count = 0, limit = PAGE_SIZE;
|
|
||||||
struct v9fs_session_info *v9ses;
|
|
||||||
|
|
||||||
spin_lock(&v9fs_sessionlist_lock);
|
|
||||||
list_for_each_entry(v9ses, &v9fs_sessionlist, slist) {
|
|
||||||
if (v9ses->cachetag) {
|
|
||||||
n = snprintf(buf, limit, "%s\n", v9ses->cachetag);
|
|
||||||
if (n < 0) {
|
|
||||||
count = n;
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
|
|
||||||
count += n;
|
|
||||||
limit -= n;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
spin_unlock(&v9fs_sessionlist_lock);
|
|
||||||
return count;
|
|
||||||
}
|
|
||||||
|
|
||||||
static struct kobj_attribute v9fs_attr_cache = __ATTR_RO(caches);
|
|
||||||
#endif /* CONFIG_9P_FSCACHE */
|
|
||||||
|
|
||||||
static struct attribute *v9fs_attrs[] = {
|
|
||||||
#ifdef CONFIG_9P_FSCACHE
|
|
||||||
&v9fs_attr_cache.attr,
|
|
||||||
#endif
|
|
||||||
NULL,
|
|
||||||
};
|
|
||||||
|
|
||||||
static struct attribute_group v9fs_attr_group = {
|
|
||||||
.attrs = v9fs_attrs,
|
|
||||||
};
|
|
||||||
|
|
||||||
/**
|
|
||||||
* v9fs_sysfs_init - Initialize the v9fs sysfs interface
|
|
||||||
*
|
|
||||||
*/
|
|
||||||
|
|
||||||
static int __init v9fs_sysfs_init(void)
|
|
||||||
{
|
|
||||||
v9fs_kobj = kobject_create_and_add("9p", fs_kobj);
|
|
||||||
if (!v9fs_kobj)
|
|
||||||
return -ENOMEM;
|
|
||||||
|
|
||||||
if (sysfs_create_group(v9fs_kobj, &v9fs_attr_group)) {
|
|
||||||
kobject_put(v9fs_kobj);
|
|
||||||
return -ENOMEM;
|
|
||||||
}
|
|
||||||
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* v9fs_sysfs_cleanup - Unregister the v9fs sysfs interface
|
|
||||||
*
|
|
||||||
*/
|
|
||||||
|
|
||||||
static void v9fs_sysfs_cleanup(void)
|
|
||||||
{
|
|
||||||
sysfs_remove_group(v9fs_kobj, &v9fs_attr_group);
|
|
||||||
kobject_put(v9fs_kobj);
|
|
||||||
}
|
|
||||||
|
|
||||||
static void v9fs_inode_init_once(void *foo)
|
|
||||||
{
|
|
||||||
struct v9fs_inode *v9inode = (struct v9fs_inode *)foo;
|
|
||||||
#ifdef CONFIG_9P_FSCACHE
|
|
||||||
v9inode->fscache = NULL;
|
|
||||||
#endif
|
|
||||||
memset(&v9inode->qid, 0, sizeof(v9inode->qid));
|
|
||||||
inode_init_once(&v9inode->vfs_inode);
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* v9fs_init_inode_cache - initialize a cache for 9P
|
|
||||||
* Returns 0 on success.
|
|
||||||
*/
|
|
||||||
static int v9fs_init_inode_cache(void)
|
|
||||||
{
|
|
||||||
v9fs_inode_cache = kmem_cache_create("v9fs_inode_cache",
|
|
||||||
sizeof(struct v9fs_inode),
|
|
||||||
0, (SLAB_RECLAIM_ACCOUNT|
|
|
||||||
SLAB_MEM_SPREAD),
|
|
||||||
v9fs_inode_init_once);
|
|
||||||
if (!v9fs_inode_cache)
|
|
||||||
return -ENOMEM;
|
|
||||||
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* v9fs_destroy_inode_cache - destroy the cache of 9P inode
|
|
||||||
*
|
|
||||||
*/
|
|
||||||
static void v9fs_destroy_inode_cache(void)
|
|
||||||
{
|
|
||||||
/*
|
|
||||||
* Make sure all delayed rcu free inodes are flushed before we
|
|
||||||
* destroy cache.
|
|
||||||
*/
|
|
||||||
rcu_barrier();
|
|
||||||
kmem_cache_destroy(v9fs_inode_cache);
|
|
||||||
}
|
|
||||||
|
|
||||||
static int v9fs_cache_register(void)
|
|
||||||
{
|
|
||||||
int ret;
|
|
||||||
ret = v9fs_init_inode_cache();
|
|
||||||
if (ret < 0)
|
|
||||||
return ret;
|
|
||||||
#ifdef CONFIG_9P_FSCACHE
|
|
||||||
ret = fscache_register_netfs(&v9fs_cache_netfs);
|
|
||||||
if (ret < 0)
|
|
||||||
v9fs_destroy_inode_cache();
|
|
||||||
#endif
|
|
||||||
return ret;
|
|
||||||
}
|
|
||||||
|
|
||||||
static void v9fs_cache_unregister(void)
|
|
||||||
{
|
|
||||||
v9fs_destroy_inode_cache();
|
|
||||||
#ifdef CONFIG_9P_FSCACHE
|
|
||||||
fscache_unregister_netfs(&v9fs_cache_netfs);
|
|
||||||
#endif
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* init_v9fs - Initialize module
|
|
||||||
*
|
|
||||||
*/
|
|
||||||
|
|
||||||
static int __init init_v9fs(void)
|
|
||||||
{
|
|
||||||
int err;
|
|
||||||
pr_info("Installing v9fs 9p2000 file system support\n");
|
|
||||||
/* TODO: Setup list of registered trasnport modules */
|
|
||||||
|
|
||||||
err = v9fs_cache_register();
|
|
||||||
if (err < 0) {
|
|
||||||
pr_err("Failed to register v9fs for caching\n");
|
|
||||||
return err;
|
|
||||||
}
|
|
||||||
|
|
||||||
err = v9fs_sysfs_init();
|
|
||||||
if (err < 0) {
|
|
||||||
pr_err("Failed to register with sysfs\n");
|
|
||||||
goto out_cache;
|
|
||||||
}
|
|
||||||
err = register_filesystem(&v9fs_fs_type);
|
|
||||||
if (err < 0) {
|
|
||||||
pr_err("Failed to register filesystem\n");
|
|
||||||
goto out_sysfs_cleanup;
|
|
||||||
}
|
|
||||||
|
|
||||||
return 0;
|
|
||||||
|
|
||||||
out_sysfs_cleanup:
|
|
||||||
v9fs_sysfs_cleanup();
|
|
||||||
|
|
||||||
out_cache:
|
|
||||||
v9fs_cache_unregister();
|
|
||||||
|
|
||||||
return err;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* exit_v9fs - shutdown module
|
|
||||||
*
|
|
||||||
*/
|
|
||||||
|
|
||||||
static void __exit exit_v9fs(void)
|
|
||||||
{
|
|
||||||
v9fs_sysfs_cleanup();
|
|
||||||
v9fs_cache_unregister();
|
|
||||||
unregister_filesystem(&v9fs_fs_type);
|
|
||||||
}
|
|
||||||
|
|
||||||
module_init(init_v9fs)
|
|
||||||
module_exit(exit_v9fs)
|
|
||||||
|
|
||||||
MODULE_AUTHOR("Latchesar Ionkov <lucho@ionkov.net>");
|
|
||||||
MODULE_AUTHOR("Eric Van Hensbergen <ericvh@gmail.com>");
|
|
||||||
MODULE_AUTHOR("Ron Minnich <rminnich@lanl.gov>");
|
|
||||||
MODULE_LICENSE("GPL");
|
|
@ -1,226 +0,0 @@
|
|||||||
/*
|
|
||||||
* V9FS definitions.
|
|
||||||
*
|
|
||||||
* Copyright (C) 2004-2008 by Eric Van Hensbergen <ericvh@gmail.com>
|
|
||||||
* Copyright (C) 2002 by Ron Minnich <rminnich@lanl.gov>
|
|
||||||
*
|
|
||||||
* This program is free software; you can redistribute it and/or modify
|
|
||||||
* it under the terms of the GNU General Public License version 2
|
|
||||||
* as published by the Free Software Foundation.
|
|
||||||
*
|
|
||||||
* This program is distributed in the hope that it will be useful,
|
|
||||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
||||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
||||||
* GNU General Public License for more details.
|
|
||||||
*
|
|
||||||
* You should have received a copy of the GNU General Public License
|
|
||||||
* along with this program; if not, write to:
|
|
||||||
* Free Software Foundation
|
|
||||||
* 51 Franklin Street, Fifth Floor
|
|
||||||
* Boston, MA 02111-1301 USA
|
|
||||||
*
|
|
||||||
*/
|
|
||||||
#ifndef FS_9P_V9FS_H
|
|
||||||
#define FS_9P_V9FS_H
|
|
||||||
|
|
||||||
#include <linux/backing-dev.h>
|
|
||||||
|
|
||||||
/**
|
|
||||||
* enum p9_session_flags - option flags for each 9P session
|
|
||||||
* @V9FS_PROTO_2000U: whether or not to use 9P2000.u extensions
|
|
||||||
* @V9FS_PROTO_2000L: whether or not to use 9P2000.l extensions
|
|
||||||
* @V9FS_ACCESS_SINGLE: only the mounting user can access the hierarchy
|
|
||||||
* @V9FS_ACCESS_USER: a new attach will be issued for every user (default)
|
|
||||||
* @V9FS_ACCESS_CLIENT: Just like user, but access check is performed on client.
|
|
||||||
* @V9FS_ACCESS_ANY: use a single attach for all users
|
|
||||||
* @V9FS_ACCESS_MASK: bit mask of different ACCESS options
|
|
||||||
* @V9FS_POSIX_ACL: POSIX ACLs are enforced
|
|
||||||
*
|
|
||||||
* Session flags reflect options selected by users at mount time
|
|
||||||
*/
|
|
||||||
#define V9FS_ACCESS_ANY (V9FS_ACCESS_SINGLE | \
|
|
||||||
V9FS_ACCESS_USER | \
|
|
||||||
V9FS_ACCESS_CLIENT)
|
|
||||||
#define V9FS_ACCESS_MASK V9FS_ACCESS_ANY
|
|
||||||
#define V9FS_ACL_MASK V9FS_POSIX_ACL
|
|
||||||
|
|
||||||
enum p9_session_flags {
|
|
||||||
V9FS_PROTO_2000U = 0x01,
|
|
||||||
V9FS_PROTO_2000L = 0x02,
|
|
||||||
V9FS_ACCESS_SINGLE = 0x04,
|
|
||||||
V9FS_ACCESS_USER = 0x08,
|
|
||||||
V9FS_ACCESS_CLIENT = 0x10,
|
|
||||||
V9FS_POSIX_ACL = 0x20
|
|
||||||
};
|
|
||||||
|
|
||||||
/* possible values of ->cache */
|
|
||||||
/**
|
|
||||||
* enum p9_cache_modes - user specified cache preferences
|
|
||||||
* @CACHE_NONE: do not cache data, dentries, or directory contents (default)
|
|
||||||
* @CACHE_LOOSE: cache data, dentries, and directory contents w/no consistency
|
|
||||||
*
|
|
||||||
* eventually support loose, tight, time, session, default always none
|
|
||||||
*/
|
|
||||||
|
|
||||||
enum p9_cache_modes {
|
|
||||||
CACHE_NONE,
|
|
||||||
CACHE_MMAP,
|
|
||||||
CACHE_LOOSE,
|
|
||||||
CACHE_FSCACHE,
|
|
||||||
};
|
|
||||||
|
|
||||||
/**
|
|
||||||
* struct v9fs_session_info - per-instance session information
|
|
||||||
* @flags: session options of type &p9_session_flags
|
|
||||||
* @nodev: set to 1 to disable device mapping
|
|
||||||
* @debug: debug level
|
|
||||||
* @afid: authentication handle
|
|
||||||
* @cache: cache mode of type &p9_cache_modes
|
|
||||||
* @cachetag: the tag of the cache associated with this session
|
|
||||||
* @fscache: session cookie associated with FS-Cache
|
|
||||||
* @uname: string user name to mount hierarchy as
|
|
||||||
* @aname: mount specifier for remote hierarchy
|
|
||||||
* @maxdata: maximum data to be sent/recvd per protocol message
|
|
||||||
* @dfltuid: default numeric userid to mount hierarchy as
|
|
||||||
* @dfltgid: default numeric groupid to mount hierarchy as
|
|
||||||
* @uid: if %V9FS_ACCESS_SINGLE, the numeric uid which mounted the hierarchy
|
|
||||||
* @clnt: reference to 9P network client instantiated for this session
|
|
||||||
* @slist: reference to list of registered 9p sessions
|
|
||||||
*
|
|
||||||
* This structure holds state for each session instance established during
|
|
||||||
* a sys_mount() .
|
|
||||||
*
|
|
||||||
* Bugs: there seems to be a lot of state which could be condensed and/or
|
|
||||||
* removed.
|
|
||||||
*/
|
|
||||||
|
|
||||||
struct v9fs_session_info {
|
|
||||||
/* options */
|
|
||||||
unsigned char flags;
|
|
||||||
unsigned char nodev;
|
|
||||||
unsigned short debug;
|
|
||||||
unsigned int afid;
|
|
||||||
unsigned int cache;
|
|
||||||
#ifdef CONFIG_9P_FSCACHE
|
|
||||||
char *cachetag;
|
|
||||||
struct fscache_cookie *fscache;
|
|
||||||
#endif
|
|
||||||
|
|
||||||
char *uname; /* user name to mount as */
|
|
||||||
char *aname; /* name of remote hierarchy being mounted */
|
|
||||||
unsigned int maxdata; /* max data for client interface */
|
|
||||||
kuid_t dfltuid; /* default uid/muid for legacy support */
|
|
||||||
kgid_t dfltgid; /* default gid for legacy support */
|
|
||||||
kuid_t uid; /* if ACCESS_SINGLE, the uid that has access */
|
|
||||||
struct p9_client *clnt; /* 9p client */
|
|
||||||
struct list_head slist; /* list of sessions registered with v9fs */
|
|
||||||
struct backing_dev_info bdi;
|
|
||||||
struct rw_semaphore rename_sem;
|
|
||||||
long session_lock_timeout; /* retry interval for blocking locks */
|
|
||||||
};
|
|
||||||
|
|
||||||
/* cache_validity flags */
|
|
||||||
#define V9FS_INO_INVALID_ATTR 0x01
|
|
||||||
|
|
||||||
struct v9fs_inode {
|
|
||||||
#ifdef CONFIG_9P_FSCACHE
|
|
||||||
struct mutex fscache_lock;
|
|
||||||
struct fscache_cookie *fscache;
|
|
||||||
#endif
|
|
||||||
struct p9_qid qid;
|
|
||||||
unsigned int cache_validity;
|
|
||||||
struct p9_fid *writeback_fid;
|
|
||||||
struct mutex v_mutex;
|
|
||||||
struct inode vfs_inode;
|
|
||||||
};
|
|
||||||
|
|
||||||
static inline struct v9fs_inode *V9FS_I(const struct inode *inode)
|
|
||||||
{
|
|
||||||
return container_of(inode, struct v9fs_inode, vfs_inode);
|
|
||||||
}
|
|
||||||
|
|
||||||
struct p9_fid *v9fs_session_init(struct v9fs_session_info *, const char *,
|
|
||||||
char *);
|
|
||||||
extern void v9fs_session_close(struct v9fs_session_info *v9ses);
|
|
||||||
extern void v9fs_session_cancel(struct v9fs_session_info *v9ses);
|
|
||||||
extern void v9fs_session_begin_cancel(struct v9fs_session_info *v9ses);
|
|
||||||
extern struct dentry *v9fs_vfs_lookup(struct inode *dir, struct dentry *dentry,
|
|
||||||
unsigned int flags);
|
|
||||||
extern int v9fs_vfs_unlink(struct inode *i, struct dentry *d);
|
|
||||||
extern int v9fs_vfs_rmdir(struct inode *i, struct dentry *d);
|
|
||||||
extern int v9fs_vfs_rename(struct inode *old_dir, struct dentry *old_dentry,
|
|
||||||
struct inode *new_dir, struct dentry *new_dentry);
|
|
||||||
extern struct inode *v9fs_inode_from_fid(struct v9fs_session_info *v9ses,
|
|
||||||
struct p9_fid *fid,
|
|
||||||
struct super_block *sb, int new);
|
|
||||||
extern const struct inode_operations v9fs_dir_inode_operations_dotl;
|
|
||||||
extern const struct inode_operations v9fs_file_inode_operations_dotl;
|
|
||||||
extern const struct inode_operations v9fs_symlink_inode_operations_dotl;
|
|
||||||
extern struct inode *v9fs_inode_from_fid_dotl(struct v9fs_session_info *v9ses,
|
|
||||||
struct p9_fid *fid,
|
|
||||||
struct super_block *sb, int new);
|
|
||||||
|
|
||||||
/* other default globals */
|
|
||||||
#define V9FS_PORT 564
|
|
||||||
#define V9FS_DEFUSER "nobody"
|
|
||||||
#define V9FS_DEFANAME ""
|
|
||||||
#define V9FS_DEFUID KUIDT_INIT(-2)
|
|
||||||
#define V9FS_DEFGID KGIDT_INIT(-2)
|
|
||||||
|
|
||||||
static inline struct v9fs_session_info *v9fs_inode2v9ses(struct inode *inode)
|
|
||||||
{
|
|
||||||
return (inode->i_sb->s_fs_info);
|
|
||||||
}
|
|
||||||
|
|
||||||
static inline struct v9fs_session_info *v9fs_dentry2v9ses(struct dentry *dentry)
|
|
||||||
{
|
|
||||||
return dentry->d_sb->s_fs_info;
|
|
||||||
}
|
|
||||||
|
|
||||||
static inline int v9fs_proto_dotu(struct v9fs_session_info *v9ses)
|
|
||||||
{
|
|
||||||
return v9ses->flags & V9FS_PROTO_2000U;
|
|
||||||
}
|
|
||||||
|
|
||||||
static inline int v9fs_proto_dotl(struct v9fs_session_info *v9ses)
|
|
||||||
{
|
|
||||||
return v9ses->flags & V9FS_PROTO_2000L;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* v9fs_get_inode_from_fid - Helper routine to populate an inode by
|
|
||||||
* issuing a attribute request
|
|
||||||
* @v9ses: session information
|
|
||||||
* @fid: fid to issue attribute request for
|
|
||||||
* @sb: superblock on which to create inode
|
|
||||||
*
|
|
||||||
*/
|
|
||||||
static inline struct inode *
|
|
||||||
v9fs_get_inode_from_fid(struct v9fs_session_info *v9ses, struct p9_fid *fid,
|
|
||||||
struct super_block *sb)
|
|
||||||
{
|
|
||||||
if (v9fs_proto_dotl(v9ses))
|
|
||||||
return v9fs_inode_from_fid_dotl(v9ses, fid, sb, 0);
|
|
||||||
else
|
|
||||||
return v9fs_inode_from_fid(v9ses, fid, sb, 0);
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* v9fs_get_new_inode_from_fid - Helper routine to populate an inode by
|
|
||||||
* issuing a attribute request
|
|
||||||
* @v9ses: session information
|
|
||||||
* @fid: fid to issue attribute request for
|
|
||||||
* @sb: superblock on which to create inode
|
|
||||||
*
|
|
||||||
*/
|
|
||||||
static inline struct inode *
|
|
||||||
v9fs_get_new_inode_from_fid(struct v9fs_session_info *v9ses, struct p9_fid *fid,
|
|
||||||
struct super_block *sb)
|
|
||||||
{
|
|
||||||
if (v9fs_proto_dotl(v9ses))
|
|
||||||
return v9fs_inode_from_fid_dotl(v9ses, fid, sb, 1);
|
|
||||||
else
|
|
||||||
return v9fs_inode_from_fid(v9ses, fid, sb, 1);
|
|
||||||
}
|
|
||||||
|
|
||||||
#endif
|
|
@ -1,105 +0,0 @@
|
|||||||
/*
|
|
||||||
* V9FS VFS extensions.
|
|
||||||
*
|
|
||||||
* Copyright (C) 2004 by Eric Van Hensbergen <ericvh@gmail.com>
|
|
||||||
* Copyright (C) 2002 by Ron Minnich <rminnich@lanl.gov>
|
|
||||||
*
|
|
||||||
* This program is free software; you can redistribute it and/or modify
|
|
||||||
* it under the terms of the GNU General Public License version 2
|
|
||||||
* as published by the Free Software Foundation.
|
|
||||||
*
|
|
||||||
* This program is distributed in the hope that it will be useful,
|
|
||||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
||||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
||||||
* GNU General Public License for more details.
|
|
||||||
*
|
|
||||||
* You should have received a copy of the GNU General Public License
|
|
||||||
* along with this program; if not, write to:
|
|
||||||
* Free Software Foundation
|
|
||||||
* 51 Franklin Street, Fifth Floor
|
|
||||||
* Boston, MA 02111-1301 USA
|
|
||||||
*
|
|
||||||
*/
|
|
||||||
#ifndef FS_9P_V9FS_VFS_H
|
|
||||||
#define FS_9P_V9FS_VFS_H
|
|
||||||
|
|
||||||
/* plan9 semantics are that created files are implicitly opened.
|
|
||||||
* But linux semantics are that you call create, then open.
|
|
||||||
* the plan9 approach is superior as it provides an atomic
|
|
||||||
* open.
|
|
||||||
* we track the create fid here. When the file is opened, if fidopen is
|
|
||||||
* non-zero, we use the fid and can skip some steps.
|
|
||||||
* there may be a better way to do this, but I don't know it.
|
|
||||||
* one BAD way is to clunk the fid on create, then open it again:
|
|
||||||
* you lose the atomicity of file open
|
|
||||||
*/
|
|
||||||
|
|
||||||
/* special case:
|
|
||||||
* unlink calls remove, which is an implicit clunk. So we have to track
|
|
||||||
* that kind of thing so that we don't try to clunk a dead fid.
|
|
||||||
*/
|
|
||||||
#define P9_LOCK_TIMEOUT (30*HZ)
|
|
||||||
|
|
||||||
/* flags for v9fs_stat2inode() & v9fs_stat2inode_dotl() */
|
|
||||||
#define V9FS_STAT2INODE_KEEP_ISIZE 1
|
|
||||||
|
|
||||||
extern struct file_system_type v9fs_fs_type;
|
|
||||||
extern const struct address_space_operations v9fs_addr_operations;
|
|
||||||
extern const struct file_operations v9fs_file_operations;
|
|
||||||
extern const struct file_operations v9fs_file_operations_dotl;
|
|
||||||
extern const struct file_operations v9fs_dir_operations;
|
|
||||||
extern const struct file_operations v9fs_dir_operations_dotl;
|
|
||||||
extern const struct dentry_operations v9fs_dentry_operations;
|
|
||||||
extern const struct dentry_operations v9fs_cached_dentry_operations;
|
|
||||||
extern const struct file_operations v9fs_cached_file_operations;
|
|
||||||
extern const struct file_operations v9fs_cached_file_operations_dotl;
|
|
||||||
extern const struct file_operations v9fs_mmap_file_operations;
|
|
||||||
extern const struct file_operations v9fs_mmap_file_operations_dotl;
|
|
||||||
extern struct kmem_cache *v9fs_inode_cache;
|
|
||||||
|
|
||||||
struct inode *v9fs_alloc_inode(struct super_block *sb);
|
|
||||||
void v9fs_destroy_inode(struct inode *inode);
|
|
||||||
struct inode *v9fs_get_inode(struct super_block *sb, umode_t mode, dev_t);
|
|
||||||
int v9fs_init_inode(struct v9fs_session_info *v9ses,
|
|
||||||
struct inode *inode, umode_t mode, dev_t);
|
|
||||||
void v9fs_evict_inode(struct inode *inode);
|
|
||||||
ino_t v9fs_qid2ino(struct p9_qid *qid);
|
|
||||||
void v9fs_stat2inode(struct p9_wstat *stat, struct inode *inode,
|
|
||||||
struct super_block *sb, unsigned int flags);
|
|
||||||
void v9fs_stat2inode_dotl(struct p9_stat_dotl *stat, struct inode *inode,
|
|
||||||
unsigned int flags);
|
|
||||||
int v9fs_dir_release(struct inode *inode, struct file *filp);
|
|
||||||
int v9fs_file_open(struct inode *inode, struct file *file);
|
|
||||||
void v9fs_inode2stat(struct inode *inode, struct p9_wstat *stat);
|
|
||||||
int v9fs_uflags2omode(int uflags, int extended);
|
|
||||||
|
|
||||||
void v9fs_blank_wstat(struct p9_wstat *wstat);
|
|
||||||
int v9fs_vfs_setattr_dotl(struct dentry *, struct iattr *);
|
|
||||||
int v9fs_file_fsync_dotl(struct file *filp, loff_t start, loff_t end,
|
|
||||||
int datasync);
|
|
||||||
int v9fs_refresh_inode(struct p9_fid *fid, struct inode *inode);
|
|
||||||
int v9fs_refresh_inode_dotl(struct p9_fid *fid, struct inode *inode);
|
|
||||||
static inline void v9fs_invalidate_inode_attr(struct inode *inode)
|
|
||||||
{
|
|
||||||
struct v9fs_inode *v9inode;
|
|
||||||
v9inode = V9FS_I(inode);
|
|
||||||
v9inode->cache_validity |= V9FS_INO_INVALID_ATTR;
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
int v9fs_open_to_dotl_flags(int flags);
|
|
||||||
|
|
||||||
static inline void v9fs_i_size_write(struct inode *inode, loff_t i_size)
|
|
||||||
{
|
|
||||||
/*
|
|
||||||
* 32-bit need the lock, concurrent updates could break the
|
|
||||||
* sequences and make i_size_read() loop forever.
|
|
||||||
* 64-bit updates are atomic and can skip the locking.
|
|
||||||
*/
|
|
||||||
if (sizeof(i_size) > sizeof(long))
|
|
||||||
spin_lock(&inode->i_lock);
|
|
||||||
i_size_write(inode, i_size);
|
|
||||||
if (sizeof(i_size) > sizeof(long))
|
|
||||||
spin_unlock(&inode->i_lock);
|
|
||||||
}
|
|
||||||
#endif
|
|
@ -1,351 +0,0 @@
|
|||||||
/*
|
|
||||||
* linux/fs/9p/vfs_addr.c
|
|
||||||
*
|
|
||||||
* This file contians vfs address (mmap) ops for 9P2000.
|
|
||||||
*
|
|
||||||
* Copyright (C) 2005 by Eric Van Hensbergen <ericvh@gmail.com>
|
|
||||||
* Copyright (C) 2002 by Ron Minnich <rminnich@lanl.gov>
|
|
||||||
*
|
|
||||||
* This program is free software; you can redistribute it and/or modify
|
|
||||||
* it under the terms of the GNU General Public License version 2
|
|
||||||
* as published by the Free Software Foundation.
|
|
||||||
*
|
|
||||||
* This program is distributed in the hope that it will be useful,
|
|
||||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
||||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
||||||
* GNU General Public License for more details.
|
|
||||||
*
|
|
||||||
* You should have received a copy of the GNU General Public License
|
|
||||||
* along with this program; if not, write to:
|
|
||||||
* Free Software Foundation
|
|
||||||
* 51 Franklin Street, Fifth Floor
|
|
||||||
* Boston, MA 02111-1301 USA
|
|
||||||
*
|
|
||||||
*/
|
|
||||||
|
|
||||||
#include <linux/module.h>
|
|
||||||
#include <linux/errno.h>
|
|
||||||
#include <linux/fs.h>
|
|
||||||
#include <linux/file.h>
|
|
||||||
#include <linux/stat.h>
|
|
||||||
#include <linux/string.h>
|
|
||||||
#include <linux/inet.h>
|
|
||||||
#include <linux/pagemap.h>
|
|
||||||
#include <linux/idr.h>
|
|
||||||
#include <linux/sched.h>
|
|
||||||
#include <linux/uio.h>
|
|
||||||
#include <net/9p/9p.h>
|
|
||||||
#include <net/9p/client.h>
|
|
||||||
|
|
||||||
#include "v9fs.h"
|
|
||||||
#include "v9fs_vfs.h"
|
|
||||||
#include "cache.h"
|
|
||||||
#include "fid.h"
|
|
||||||
|
|
||||||
/**
|
|
||||||
* v9fs_fid_readpage - read an entire page in from 9P
|
|
||||||
*
|
|
||||||
* @fid: fid being read
|
|
||||||
* @page: structure to page
|
|
||||||
*
|
|
||||||
*/
|
|
||||||
static int v9fs_fid_readpage(struct p9_fid *fid, struct page *page)
|
|
||||||
{
|
|
||||||
struct inode *inode = page->mapping->host;
|
|
||||||
struct bio_vec bvec = {.bv_page = page, .bv_len = PAGE_SIZE};
|
|
||||||
struct iov_iter to;
|
|
||||||
int retval, err;
|
|
||||||
|
|
||||||
p9_debug(P9_DEBUG_VFS, "\n");
|
|
||||||
|
|
||||||
BUG_ON(!PageLocked(page));
|
|
||||||
|
|
||||||
retval = v9fs_readpage_from_fscache(inode, page);
|
|
||||||
if (retval == 0)
|
|
||||||
return retval;
|
|
||||||
|
|
||||||
iov_iter_bvec(&to, ITER_BVEC | READ, &bvec, 1, PAGE_SIZE);
|
|
||||||
|
|
||||||
retval = p9_client_read(fid, page_offset(page), &to, &err);
|
|
||||||
if (err) {
|
|
||||||
v9fs_uncache_page(inode, page);
|
|
||||||
retval = err;
|
|
||||||
goto done;
|
|
||||||
}
|
|
||||||
|
|
||||||
zero_user(page, retval, PAGE_SIZE - retval);
|
|
||||||
flush_dcache_page(page);
|
|
||||||
SetPageUptodate(page);
|
|
||||||
|
|
||||||
v9fs_readpage_to_fscache(inode, page);
|
|
||||||
retval = 0;
|
|
||||||
|
|
||||||
done:
|
|
||||||
unlock_page(page);
|
|
||||||
return retval;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* v9fs_vfs_readpage - read an entire page in from 9P
|
|
||||||
*
|
|
||||||
* @filp: file being read
|
|
||||||
* @page: structure to page
|
|
||||||
*
|
|
||||||
*/
|
|
||||||
|
|
||||||
static int v9fs_vfs_readpage(struct file *filp, struct page *page)
|
|
||||||
{
|
|
||||||
return v9fs_fid_readpage(filp->private_data, page);
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* v9fs_vfs_readpages - read a set of pages from 9P
|
|
||||||
*
|
|
||||||
* @filp: file being read
|
|
||||||
* @mapping: the address space
|
|
||||||
* @pages: list of pages to read
|
|
||||||
* @nr_pages: count of pages to read
|
|
||||||
*
|
|
||||||
*/
|
|
||||||
|
|
||||||
static int v9fs_vfs_readpages(struct file *filp, struct address_space *mapping,
|
|
||||||
struct list_head *pages, unsigned nr_pages)
|
|
||||||
{
|
|
||||||
int ret = 0;
|
|
||||||
struct inode *inode;
|
|
||||||
|
|
||||||
inode = mapping->host;
|
|
||||||
p9_debug(P9_DEBUG_VFS, "inode: %p file: %p\n", inode, filp);
|
|
||||||
|
|
||||||
ret = v9fs_readpages_from_fscache(inode, mapping, pages, &nr_pages);
|
|
||||||
if (ret == 0)
|
|
||||||
return ret;
|
|
||||||
|
|
||||||
ret = read_cache_pages(mapping, pages, (void *)v9fs_vfs_readpage, filp);
|
|
||||||
p9_debug(P9_DEBUG_VFS, " = %d\n", ret);
|
|
||||||
return ret;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* v9fs_release_page - release the private state associated with a page
|
|
||||||
*
|
|
||||||
* Returns 1 if the page can be released, false otherwise.
|
|
||||||
*/
|
|
||||||
|
|
||||||
static int v9fs_release_page(struct page *page, gfp_t gfp)
|
|
||||||
{
|
|
||||||
if (PagePrivate(page))
|
|
||||||
return 0;
|
|
||||||
return v9fs_fscache_release_page(page, gfp);
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* v9fs_invalidate_page - Invalidate a page completely or partially
|
|
||||||
*
|
|
||||||
* @page: structure to page
|
|
||||||
* @offset: offset in the page
|
|
||||||
*/
|
|
||||||
|
|
||||||
static void v9fs_invalidate_page(struct page *page, unsigned int offset,
|
|
||||||
unsigned int length)
|
|
||||||
{
|
|
||||||
/*
|
|
||||||
* If called with zero offset, we should release
|
|
||||||
* the private state assocated with the page
|
|
||||||
*/
|
|
||||||
if (offset == 0 && length == PAGE_CACHE_SIZE)
|
|
||||||
v9fs_fscache_invalidate_page(page);
|
|
||||||
}
|
|
||||||
|
|
||||||
static int v9fs_vfs_writepage_locked(struct page *page)
|
|
||||||
{
|
|
||||||
struct inode *inode = page->mapping->host;
|
|
||||||
struct v9fs_inode *v9inode = V9FS_I(inode);
|
|
||||||
loff_t size = i_size_read(inode);
|
|
||||||
struct iov_iter from;
|
|
||||||
struct bio_vec bvec;
|
|
||||||
int err, len;
|
|
||||||
|
|
||||||
if (page->index == size >> PAGE_CACHE_SHIFT)
|
|
||||||
len = size & ~PAGE_CACHE_MASK;
|
|
||||||
else
|
|
||||||
len = PAGE_CACHE_SIZE;
|
|
||||||
|
|
||||||
bvec.bv_page = page;
|
|
||||||
bvec.bv_offset = 0;
|
|
||||||
bvec.bv_len = len;
|
|
||||||
iov_iter_bvec(&from, ITER_BVEC | WRITE, &bvec, 1, len);
|
|
||||||
|
|
||||||
/* We should have writeback_fid always set */
|
|
||||||
BUG_ON(!v9inode->writeback_fid);
|
|
||||||
|
|
||||||
set_page_writeback(page);
|
|
||||||
|
|
||||||
p9_client_write(v9inode->writeback_fid, page_offset(page), &from, &err);
|
|
||||||
|
|
||||||
end_page_writeback(page);
|
|
||||||
return err;
|
|
||||||
}
|
|
||||||
|
|
||||||
static int v9fs_vfs_writepage(struct page *page, struct writeback_control *wbc)
|
|
||||||
{
|
|
||||||
int retval;
|
|
||||||
|
|
||||||
p9_debug(P9_DEBUG_VFS, "page %p\n", page);
|
|
||||||
|
|
||||||
retval = v9fs_vfs_writepage_locked(page);
|
|
||||||
if (retval < 0) {
|
|
||||||
if (retval == -EAGAIN) {
|
|
||||||
redirty_page_for_writepage(wbc, page);
|
|
||||||
retval = 0;
|
|
||||||
} else {
|
|
||||||
SetPageError(page);
|
|
||||||
mapping_set_error(page->mapping, retval);
|
|
||||||
}
|
|
||||||
} else
|
|
||||||
retval = 0;
|
|
||||||
|
|
||||||
unlock_page(page);
|
|
||||||
return retval;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* v9fs_launder_page - Writeback a dirty page
|
|
||||||
* Returns 0 on success.
|
|
||||||
*/
|
|
||||||
|
|
||||||
static int v9fs_launder_page(struct page *page)
|
|
||||||
{
|
|
||||||
int retval;
|
|
||||||
struct inode *inode = page->mapping->host;
|
|
||||||
|
|
||||||
v9fs_fscache_wait_on_page_write(inode, page);
|
|
||||||
if (clear_page_dirty_for_io(page)) {
|
|
||||||
retval = v9fs_vfs_writepage_locked(page);
|
|
||||||
if (retval)
|
|
||||||
return retval;
|
|
||||||
}
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* v9fs_direct_IO - 9P address space operation for direct I/O
|
|
||||||
* @iocb: target I/O control block
|
|
||||||
* @pos: offset in file to begin the operation
|
|
||||||
*
|
|
||||||
* The presence of v9fs_direct_IO() in the address space ops vector
|
|
||||||
* allowes open() O_DIRECT flags which would have failed otherwise.
|
|
||||||
*
|
|
||||||
* In the non-cached mode, we shunt off direct read and write requests before
|
|
||||||
* the VFS gets them, so this method should never be called.
|
|
||||||
*
|
|
||||||
* Direct IO is not 'yet' supported in the cached mode. Hence when
|
|
||||||
* this routine is called through generic_file_aio_read(), the read/write fails
|
|
||||||
* with an error.
|
|
||||||
*
|
|
||||||
*/
|
|
||||||
static ssize_t
|
|
||||||
v9fs_direct_IO(struct kiocb *iocb, struct iov_iter *iter, loff_t pos)
|
|
||||||
{
|
|
||||||
struct file *file = iocb->ki_filp;
|
|
||||||
ssize_t n;
|
|
||||||
int err = 0;
|
|
||||||
if (iov_iter_rw(iter) == WRITE) {
|
|
||||||
n = p9_client_write(file->private_data, pos, iter, &err);
|
|
||||||
if (n) {
|
|
||||||
struct inode *inode = file_inode(file);
|
|
||||||
loff_t i_size = i_size_read(inode);
|
|
||||||
if (pos + n > i_size)
|
|
||||||
inode_add_bytes(inode, pos + n - i_size);
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
n = p9_client_read(file->private_data, pos, iter, &err);
|
|
||||||
}
|
|
||||||
return n ? n : err;
|
|
||||||
}
|
|
||||||
|
|
||||||
static int v9fs_write_begin(struct file *filp, struct address_space *mapping,
|
|
||||||
loff_t pos, unsigned len, unsigned flags,
|
|
||||||
struct page **pagep, void **fsdata)
|
|
||||||
{
|
|
||||||
int retval = 0;
|
|
||||||
struct page *page;
|
|
||||||
struct v9fs_inode *v9inode;
|
|
||||||
pgoff_t index = pos >> PAGE_CACHE_SHIFT;
|
|
||||||
struct inode *inode = mapping->host;
|
|
||||||
|
|
||||||
|
|
||||||
p9_debug(P9_DEBUG_VFS, "filp %p, mapping %p\n", filp, mapping);
|
|
||||||
|
|
||||||
v9inode = V9FS_I(inode);
|
|
||||||
start:
|
|
||||||
page = grab_cache_page_write_begin(mapping, index, flags);
|
|
||||||
if (!page) {
|
|
||||||
retval = -ENOMEM;
|
|
||||||
goto out;
|
|
||||||
}
|
|
||||||
BUG_ON(!v9inode->writeback_fid);
|
|
||||||
if (PageUptodate(page))
|
|
||||||
goto out;
|
|
||||||
|
|
||||||
if (len == PAGE_CACHE_SIZE)
|
|
||||||
goto out;
|
|
||||||
|
|
||||||
retval = v9fs_fid_readpage(v9inode->writeback_fid, page);
|
|
||||||
page_cache_release(page);
|
|
||||||
if (!retval)
|
|
||||||
goto start;
|
|
||||||
out:
|
|
||||||
*pagep = page;
|
|
||||||
return retval;
|
|
||||||
}
|
|
||||||
|
|
||||||
static int v9fs_write_end(struct file *filp, struct address_space *mapping,
|
|
||||||
loff_t pos, unsigned len, unsigned copied,
|
|
||||||
struct page *page, void *fsdata)
|
|
||||||
{
|
|
||||||
loff_t last_pos = pos + copied;
|
|
||||||
struct inode *inode = page->mapping->host;
|
|
||||||
|
|
||||||
p9_debug(P9_DEBUG_VFS, "filp %p, mapping %p\n", filp, mapping);
|
|
||||||
|
|
||||||
if (unlikely(copied < len)) {
|
|
||||||
/*
|
|
||||||
* zero out the rest of the area
|
|
||||||
*/
|
|
||||||
unsigned from = pos & (PAGE_CACHE_SIZE - 1);
|
|
||||||
|
|
||||||
zero_user(page, from + copied, len - copied);
|
|
||||||
flush_dcache_page(page);
|
|
||||||
}
|
|
||||||
|
|
||||||
if (!PageUptodate(page))
|
|
||||||
SetPageUptodate(page);
|
|
||||||
/*
|
|
||||||
* No need to use i_size_read() here, the i_size
|
|
||||||
* cannot change under us because we hold the i_mutex.
|
|
||||||
*/
|
|
||||||
if (last_pos > inode->i_size) {
|
|
||||||
inode_add_bytes(inode, last_pos - inode->i_size);
|
|
||||||
i_size_write(inode, last_pos);
|
|
||||||
}
|
|
||||||
set_page_dirty(page);
|
|
||||||
unlock_page(page);
|
|
||||||
page_cache_release(page);
|
|
||||||
|
|
||||||
return copied;
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
const struct address_space_operations v9fs_addr_operations = {
|
|
||||||
.readpage = v9fs_vfs_readpage,
|
|
||||||
.readpages = v9fs_vfs_readpages,
|
|
||||||
.set_page_dirty = __set_page_dirty_nobuffers,
|
|
||||||
.writepage = v9fs_vfs_writepage,
|
|
||||||
.write_begin = v9fs_write_begin,
|
|
||||||
.write_end = v9fs_write_end,
|
|
||||||
.releasepage = v9fs_release_page,
|
|
||||||
.invalidatepage = v9fs_invalidate_page,
|
|
||||||
.launder_page = v9fs_launder_page,
|
|
||||||
.direct_IO = v9fs_direct_IO,
|
|
||||||
};
|
|
@ -1,122 +0,0 @@
|
|||||||
/*
|
|
||||||
* linux/fs/9p/vfs_dentry.c
|
|
||||||
*
|
|
||||||
* This file contians vfs dentry ops for the 9P2000 protocol.
|
|
||||||
*
|
|
||||||
* Copyright (C) 2004 by Eric Van Hensbergen <ericvh@gmail.com>
|
|
||||||
* Copyright (C) 2002 by Ron Minnich <rminnich@lanl.gov>
|
|
||||||
*
|
|
||||||
* This program is free software; you can redistribute it and/or modify
|
|
||||||
* it under the terms of the GNU General Public License version 2
|
|
||||||
* as published by the Free Software Foundation.
|
|
||||||
*
|
|
||||||
* This program is distributed in the hope that it will be useful,
|
|
||||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
||||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
||||||
* GNU General Public License for more details.
|
|
||||||
*
|
|
||||||
* You should have received a copy of the GNU General Public License
|
|
||||||
* along with this program; if not, write to:
|
|
||||||
* Free Software Foundation
|
|
||||||
* 51 Franklin Street, Fifth Floor
|
|
||||||
* Boston, MA 02111-1301 USA
|
|
||||||
*
|
|
||||||
*/
|
|
||||||
|
|
||||||
#include <linux/module.h>
|
|
||||||
#include <linux/errno.h>
|
|
||||||
#include <linux/fs.h>
|
|
||||||
#include <linux/file.h>
|
|
||||||
#include <linux/pagemap.h>
|
|
||||||
#include <linux/stat.h>
|
|
||||||
#include <linux/string.h>
|
|
||||||
#include <linux/inet.h>
|
|
||||||
#include <linux/namei.h>
|
|
||||||
#include <linux/idr.h>
|
|
||||||
#include <linux/sched.h>
|
|
||||||
#include <linux/slab.h>
|
|
||||||
#include <net/9p/9p.h>
|
|
||||||
#include <net/9p/client.h>
|
|
||||||
|
|
||||||
#include "v9fs.h"
|
|
||||||
#include "v9fs_vfs.h"
|
|
||||||
#include "fid.h"
|
|
||||||
|
|
||||||
/**
|
|
||||||
* v9fs_cached_dentry_delete - called when dentry refcount equals 0
|
|
||||||
* @dentry: dentry in question
|
|
||||||
*
|
|
||||||
*/
|
|
||||||
static int v9fs_cached_dentry_delete(const struct dentry *dentry)
|
|
||||||
{
|
|
||||||
p9_debug(P9_DEBUG_VFS, " dentry: %pd (%p)\n",
|
|
||||||
dentry, dentry);
|
|
||||||
|
|
||||||
/* Don't cache negative dentries */
|
|
||||||
if (d_really_is_negative(dentry))
|
|
||||||
return 1;
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* v9fs_dentry_release - called when dentry is going to be freed
|
|
||||||
* @dentry: dentry that is being release
|
|
||||||
*
|
|
||||||
*/
|
|
||||||
|
|
||||||
static void v9fs_dentry_release(struct dentry *dentry)
|
|
||||||
{
|
|
||||||
struct hlist_node *p, *n;
|
|
||||||
p9_debug(P9_DEBUG_VFS, " dentry: %pd (%p)\n",
|
|
||||||
dentry, dentry);
|
|
||||||
hlist_for_each_safe(p, n, (struct hlist_head *)&dentry->d_fsdata)
|
|
||||||
p9_client_clunk(hlist_entry(p, struct p9_fid, dlist));
|
|
||||||
dentry->d_fsdata = NULL;
|
|
||||||
}
|
|
||||||
|
|
||||||
static int v9fs_lookup_revalidate(struct dentry *dentry, unsigned int flags)
|
|
||||||
{
|
|
||||||
struct p9_fid *fid;
|
|
||||||
struct inode *inode;
|
|
||||||
struct v9fs_inode *v9inode;
|
|
||||||
|
|
||||||
if (flags & LOOKUP_RCU)
|
|
||||||
return -ECHILD;
|
|
||||||
|
|
||||||
inode = d_inode(dentry);
|
|
||||||
if (!inode)
|
|
||||||
goto out_valid;
|
|
||||||
|
|
||||||
v9inode = V9FS_I(inode);
|
|
||||||
if (v9inode->cache_validity & V9FS_INO_INVALID_ATTR) {
|
|
||||||
int retval;
|
|
||||||
struct v9fs_session_info *v9ses;
|
|
||||||
fid = v9fs_fid_lookup(dentry);
|
|
||||||
if (IS_ERR(fid))
|
|
||||||
return PTR_ERR(fid);
|
|
||||||
|
|
||||||
v9ses = v9fs_inode2v9ses(inode);
|
|
||||||
if (v9fs_proto_dotl(v9ses))
|
|
||||||
retval = v9fs_refresh_inode_dotl(fid, inode);
|
|
||||||
else
|
|
||||||
retval = v9fs_refresh_inode(fid, inode);
|
|
||||||
if (retval == -ENOENT)
|
|
||||||
return 0;
|
|
||||||
if (retval < 0)
|
|
||||||
return retval;
|
|
||||||
}
|
|
||||||
out_valid:
|
|
||||||
return 1;
|
|
||||||
}
|
|
||||||
|
|
||||||
const struct dentry_operations v9fs_cached_dentry_operations = {
|
|
||||||
.d_revalidate = v9fs_lookup_revalidate,
|
|
||||||
.d_weak_revalidate = v9fs_lookup_revalidate,
|
|
||||||
.d_delete = v9fs_cached_dentry_delete,
|
|
||||||
.d_release = v9fs_dentry_release,
|
|
||||||
};
|
|
||||||
|
|
||||||
const struct dentry_operations v9fs_dentry_operations = {
|
|
||||||
.d_delete = always_delete_dentry,
|
|
||||||
.d_release = v9fs_dentry_release,
|
|
||||||
};
|
|
@ -1,248 +0,0 @@
|
|||||||
/*
|
|
||||||
* linux/fs/9p/vfs_dir.c
|
|
||||||
*
|
|
||||||
* This file contains vfs directory ops for the 9P2000 protocol.
|
|
||||||
*
|
|
||||||
* Copyright (C) 2004 by Eric Van Hensbergen <ericvh@gmail.com>
|
|
||||||
* Copyright (C) 2002 by Ron Minnich <rminnich@lanl.gov>
|
|
||||||
*
|
|
||||||
* This program is free software; you can redistribute it and/or modify
|
|
||||||
* it under the terms of the GNU General Public License version 2
|
|
||||||
* as published by the Free Software Foundation.
|
|
||||||
*
|
|
||||||
* This program is distributed in the hope that it will be useful,
|
|
||||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
||||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
||||||
* GNU General Public License for more details.
|
|
||||||
*
|
|
||||||
* You should have received a copy of the GNU General Public License
|
|
||||||
* along with this program; if not, write to:
|
|
||||||
* Free Software Foundation
|
|
||||||
* 51 Franklin Street, Fifth Floor
|
|
||||||
* Boston, MA 02111-1301 USA
|
|
||||||
*
|
|
||||||
*/
|
|
||||||
|
|
||||||
#include <linux/module.h>
|
|
||||||
#include <linux/errno.h>
|
|
||||||
#include <linux/fs.h>
|
|
||||||
#include <linux/file.h>
|
|
||||||
#include <linux/stat.h>
|
|
||||||
#include <linux/string.h>
|
|
||||||
#include <linux/sched.h>
|
|
||||||
#include <linux/inet.h>
|
|
||||||
#include <linux/idr.h>
|
|
||||||
#include <linux/slab.h>
|
|
||||||
#include <linux/uio.h>
|
|
||||||
#include <net/9p/9p.h>
|
|
||||||
#include <net/9p/client.h>
|
|
||||||
|
|
||||||
#include "v9fs.h"
|
|
||||||
#include "v9fs_vfs.h"
|
|
||||||
#include "fid.h"
|
|
||||||
|
|
||||||
/**
|
|
||||||
* struct p9_rdir - readdir accounting
|
|
||||||
* @head: start offset of current dirread buffer
|
|
||||||
* @tail: end offset of current dirread buffer
|
|
||||||
* @buf: dirread buffer
|
|
||||||
*
|
|
||||||
* private structure for keeping track of readdir
|
|
||||||
* allocated on demand
|
|
||||||
*/
|
|
||||||
|
|
||||||
struct p9_rdir {
|
|
||||||
int head;
|
|
||||||
int tail;
|
|
||||||
uint8_t buf[];
|
|
||||||
};
|
|
||||||
|
|
||||||
/**
|
|
||||||
* dt_type - return file type
|
|
||||||
* @mistat: mistat structure
|
|
||||||
*
|
|
||||||
*/
|
|
||||||
|
|
||||||
static inline int dt_type(struct p9_wstat *mistat)
|
|
||||||
{
|
|
||||||
unsigned long perm = mistat->mode;
|
|
||||||
int rettype = DT_REG;
|
|
||||||
|
|
||||||
if (perm & P9_DMDIR)
|
|
||||||
rettype = DT_DIR;
|
|
||||||
if (perm & P9_DMSYMLINK)
|
|
||||||
rettype = DT_LNK;
|
|
||||||
|
|
||||||
return rettype;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* v9fs_alloc_rdir_buf - Allocate buffer used for read and readdir
|
|
||||||
* @filp: opened file structure
|
|
||||||
* @buflen: Length in bytes of buffer to allocate
|
|
||||||
*
|
|
||||||
*/
|
|
||||||
|
|
||||||
static struct p9_rdir *v9fs_alloc_rdir_buf(struct file *filp, int buflen)
|
|
||||||
{
|
|
||||||
struct p9_fid *fid = filp->private_data;
|
|
||||||
if (!fid->rdir)
|
|
||||||
fid->rdir = kzalloc(sizeof(struct p9_rdir) + buflen, GFP_KERNEL);
|
|
||||||
return fid->rdir;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* v9fs_dir_readdir - iterate through a directory
|
|
||||||
* @file: opened file structure
|
|
||||||
* @ctx: actor we feed the entries to
|
|
||||||
*
|
|
||||||
*/
|
|
||||||
|
|
||||||
static int v9fs_dir_readdir(struct file *file, struct dir_context *ctx)
|
|
||||||
{
|
|
||||||
bool over;
|
|
||||||
struct p9_wstat st;
|
|
||||||
int err = 0;
|
|
||||||
struct p9_fid *fid;
|
|
||||||
int buflen;
|
|
||||||
struct p9_rdir *rdir;
|
|
||||||
struct kvec kvec;
|
|
||||||
|
|
||||||
p9_debug(P9_DEBUG_VFS, "name %pD\n", file);
|
|
||||||
fid = file->private_data;
|
|
||||||
|
|
||||||
buflen = fid->clnt->msize - P9_IOHDRSZ;
|
|
||||||
|
|
||||||
rdir = v9fs_alloc_rdir_buf(file, buflen);
|
|
||||||
if (!rdir)
|
|
||||||
return -ENOMEM;
|
|
||||||
kvec.iov_base = rdir->buf;
|
|
||||||
kvec.iov_len = buflen;
|
|
||||||
|
|
||||||
while (1) {
|
|
||||||
if (rdir->tail == rdir->head) {
|
|
||||||
struct iov_iter to;
|
|
||||||
int n;
|
|
||||||
iov_iter_kvec(&to, READ | ITER_KVEC, &kvec, 1, buflen);
|
|
||||||
n = p9_client_read(file->private_data, ctx->pos, &to,
|
|
||||||
&err);
|
|
||||||
if (err)
|
|
||||||
return err;
|
|
||||||
if (n == 0)
|
|
||||||
return 0;
|
|
||||||
|
|
||||||
rdir->head = 0;
|
|
||||||
rdir->tail = n;
|
|
||||||
}
|
|
||||||
while (rdir->head < rdir->tail) {
|
|
||||||
err = p9stat_read(fid->clnt, rdir->buf + rdir->head,
|
|
||||||
rdir->tail - rdir->head, &st);
|
|
||||||
if (err <= 0) {
|
|
||||||
p9_debug(P9_DEBUG_VFS, "returned %d\n", err);
|
|
||||||
return -EIO;
|
|
||||||
}
|
|
||||||
|
|
||||||
over = !dir_emit(ctx, st.name, strlen(st.name),
|
|
||||||
v9fs_qid2ino(&st.qid), dt_type(&st));
|
|
||||||
p9stat_free(&st);
|
|
||||||
if (over)
|
|
||||||
return 0;
|
|
||||||
|
|
||||||
rdir->head += err;
|
|
||||||
ctx->pos += err;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* v9fs_dir_readdir_dotl - iterate through a directory
|
|
||||||
* @file: opened file structure
|
|
||||||
* @ctx: actor we feed the entries to
|
|
||||||
*
|
|
||||||
*/
|
|
||||||
static int v9fs_dir_readdir_dotl(struct file *file, struct dir_context *ctx)
|
|
||||||
{
|
|
||||||
int err = 0;
|
|
||||||
struct p9_fid *fid;
|
|
||||||
int buflen;
|
|
||||||
struct p9_rdir *rdir;
|
|
||||||
struct p9_dirent curdirent;
|
|
||||||
|
|
||||||
p9_debug(P9_DEBUG_VFS, "name %pD\n", file);
|
|
||||||
fid = file->private_data;
|
|
||||||
|
|
||||||
buflen = fid->clnt->msize - P9_READDIRHDRSZ;
|
|
||||||
|
|
||||||
rdir = v9fs_alloc_rdir_buf(file, buflen);
|
|
||||||
if (!rdir)
|
|
||||||
return -ENOMEM;
|
|
||||||
|
|
||||||
while (1) {
|
|
||||||
if (rdir->tail == rdir->head) {
|
|
||||||
err = p9_client_readdir(fid, rdir->buf, buflen,
|
|
||||||
ctx->pos);
|
|
||||||
if (err <= 0)
|
|
||||||
return err;
|
|
||||||
|
|
||||||
rdir->head = 0;
|
|
||||||
rdir->tail = err;
|
|
||||||
}
|
|
||||||
|
|
||||||
while (rdir->head < rdir->tail) {
|
|
||||||
|
|
||||||
err = p9dirent_read(fid->clnt, rdir->buf + rdir->head,
|
|
||||||
rdir->tail - rdir->head,
|
|
||||||
&curdirent);
|
|
||||||
if (err < 0) {
|
|
||||||
p9_debug(P9_DEBUG_VFS, "returned %d\n", err);
|
|
||||||
return -EIO;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (!dir_emit(ctx, curdirent.d_name,
|
|
||||||
strlen(curdirent.d_name),
|
|
||||||
v9fs_qid2ino(&curdirent.qid),
|
|
||||||
curdirent.d_type))
|
|
||||||
return 0;
|
|
||||||
|
|
||||||
ctx->pos = curdirent.d_off;
|
|
||||||
rdir->head += err;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
/**
|
|
||||||
* v9fs_dir_release - close a directory
|
|
||||||
* @inode: inode of the directory
|
|
||||||
* @filp: file pointer to a directory
|
|
||||||
*
|
|
||||||
*/
|
|
||||||
|
|
||||||
int v9fs_dir_release(struct inode *inode, struct file *filp)
|
|
||||||
{
|
|
||||||
struct p9_fid *fid;
|
|
||||||
|
|
||||||
fid = filp->private_data;
|
|
||||||
p9_debug(P9_DEBUG_VFS, "inode: %p filp: %p fid: %d\n",
|
|
||||||
inode, filp, fid ? fid->fid : -1);
|
|
||||||
if (fid)
|
|
||||||
p9_client_clunk(fid);
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
const struct file_operations v9fs_dir_operations = {
|
|
||||||
.read = generic_read_dir,
|
|
||||||
.llseek = generic_file_llseek,
|
|
||||||
.iterate = v9fs_dir_readdir,
|
|
||||||
.open = v9fs_file_open,
|
|
||||||
.release = v9fs_dir_release,
|
|
||||||
};
|
|
||||||
|
|
||||||
const struct file_operations v9fs_dir_operations_dotl = {
|
|
||||||
.read = generic_read_dir,
|
|
||||||
.llseek = generic_file_llseek,
|
|
||||||
.iterate = v9fs_dir_readdir_dotl,
|
|
||||||
.open = v9fs_file_open,
|
|
||||||
.release = v9fs_dir_release,
|
|
||||||
.fsync = v9fs_file_fsync_dotl,
|
|
||||||
};
|
|
@ -1,726 +0,0 @@
|
|||||||
/*
|
|
||||||
* linux/fs/9p/vfs_file.c
|
|
||||||
*
|
|
||||||
* This file contians vfs file ops for 9P2000.
|
|
||||||
*
|
|
||||||
* Copyright (C) 2004 by Eric Van Hensbergen <ericvh@gmail.com>
|
|
||||||
* Copyright (C) 2002 by Ron Minnich <rminnich@lanl.gov>
|
|
||||||
*
|
|
||||||
* This program is free software; you can redistribute it and/or modify
|
|
||||||
* it under the terms of the GNU General Public License version 2
|
|
||||||
* as published by the Free Software Foundation.
|
|
||||||
*
|
|
||||||
* This program is distributed in the hope that it will be useful,
|
|
||||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
||||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
||||||
* GNU General Public License for more details.
|
|
||||||
*
|
|
||||||
* You should have received a copy of the GNU General Public License
|
|
||||||
* along with this program; if not, write to:
|
|
||||||
* Free Software Foundation
|
|
||||||
* 51 Franklin Street, Fifth Floor
|
|
||||||
* Boston, MA 02111-1301 USA
|
|
||||||
*
|
|
||||||
*/
|
|
||||||
|
|
||||||
#include <linux/module.h>
|
|
||||||
#include <linux/errno.h>
|
|
||||||
#include <linux/fs.h>
|
|
||||||
#include <linux/sched.h>
|
|
||||||
#include <linux/file.h>
|
|
||||||
#include <linux/stat.h>
|
|
||||||
#include <linux/string.h>
|
|
||||||
#include <linux/inet.h>
|
|
||||||
#include <linux/list.h>
|
|
||||||
#include <linux/pagemap.h>
|
|
||||||
#include <linux/utsname.h>
|
|
||||||
#include <asm/uaccess.h>
|
|
||||||
#include <linux/idr.h>
|
|
||||||
#include <linux/uio.h>
|
|
||||||
#include <linux/slab.h>
|
|
||||||
#include <net/9p/9p.h>
|
|
||||||
#include <net/9p/client.h>
|
|
||||||
|
|
||||||
#include "v9fs.h"
|
|
||||||
#include "v9fs_vfs.h"
|
|
||||||
#include "fid.h"
|
|
||||||
#include "cache.h"
|
|
||||||
|
|
||||||
static const struct vm_operations_struct v9fs_file_vm_ops;
|
|
||||||
static const struct vm_operations_struct v9fs_mmap_file_vm_ops;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* v9fs_file_open - open a file (or directory)
|
|
||||||
* @inode: inode to be opened
|
|
||||||
* @file: file being opened
|
|
||||||
*
|
|
||||||
*/
|
|
||||||
|
|
||||||
int v9fs_file_open(struct inode *inode, struct file *file)
|
|
||||||
{
|
|
||||||
int err;
|
|
||||||
struct v9fs_inode *v9inode;
|
|
||||||
struct v9fs_session_info *v9ses;
|
|
||||||
struct p9_fid *fid;
|
|
||||||
int omode;
|
|
||||||
|
|
||||||
p9_debug(P9_DEBUG_VFS, "inode: %p file: %p\n", inode, file);
|
|
||||||
v9inode = V9FS_I(inode);
|
|
||||||
v9ses = v9fs_inode2v9ses(inode);
|
|
||||||
if (v9fs_proto_dotl(v9ses))
|
|
||||||
omode = v9fs_open_to_dotl_flags(file->f_flags);
|
|
||||||
else
|
|
||||||
omode = v9fs_uflags2omode(file->f_flags,
|
|
||||||
v9fs_proto_dotu(v9ses));
|
|
||||||
fid = file->private_data;
|
|
||||||
if (!fid) {
|
|
||||||
fid = v9fs_fid_clone(file_dentry(file));
|
|
||||||
if (IS_ERR(fid))
|
|
||||||
return PTR_ERR(fid);
|
|
||||||
|
|
||||||
err = p9_client_open(fid, omode);
|
|
||||||
if (err < 0) {
|
|
||||||
p9_client_clunk(fid);
|
|
||||||
return err;
|
|
||||||
}
|
|
||||||
if ((file->f_flags & O_APPEND) &&
|
|
||||||
(!v9fs_proto_dotu(v9ses) && !v9fs_proto_dotl(v9ses)))
|
|
||||||
generic_file_llseek(file, 0, SEEK_END);
|
|
||||||
}
|
|
||||||
|
|
||||||
file->private_data = fid;
|
|
||||||
mutex_lock(&v9inode->v_mutex);
|
|
||||||
if ((v9ses->cache == CACHE_LOOSE || v9ses->cache == CACHE_FSCACHE) &&
|
|
||||||
!v9inode->writeback_fid &&
|
|
||||||
((file->f_flags & O_ACCMODE) != O_RDONLY)) {
|
|
||||||
/*
|
|
||||||
* clone a fid and add it to writeback_fid
|
|
||||||
* we do it during open time instead of
|
|
||||||
* page dirty time via write_begin/page_mkwrite
|
|
||||||
* because we want write after unlink usecase
|
|
||||||
* to work.
|
|
||||||
*/
|
|
||||||
fid = v9fs_writeback_fid(file_dentry(file));
|
|
||||||
if (IS_ERR(fid)) {
|
|
||||||
err = PTR_ERR(fid);
|
|
||||||
mutex_unlock(&v9inode->v_mutex);
|
|
||||||
goto out_error;
|
|
||||||
}
|
|
||||||
v9inode->writeback_fid = (void *) fid;
|
|
||||||
}
|
|
||||||
mutex_unlock(&v9inode->v_mutex);
|
|
||||||
if (v9ses->cache == CACHE_LOOSE || v9ses->cache == CACHE_FSCACHE)
|
|
||||||
v9fs_cache_inode_set_cookie(inode, file);
|
|
||||||
return 0;
|
|
||||||
out_error:
|
|
||||||
p9_client_clunk(file->private_data);
|
|
||||||
file->private_data = NULL;
|
|
||||||
return err;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* v9fs_file_lock - lock a file (or directory)
|
|
||||||
* @filp: file to be locked
|
|
||||||
* @cmd: lock command
|
|
||||||
* @fl: file lock structure
|
|
||||||
*
|
|
||||||
* Bugs: this looks like a local only lock, we should extend into 9P
|
|
||||||
* by using open exclusive
|
|
||||||
*/
|
|
||||||
|
|
||||||
static int v9fs_file_lock(struct file *filp, int cmd, struct file_lock *fl)
|
|
||||||
{
|
|
||||||
int res = 0;
|
|
||||||
struct inode *inode = file_inode(filp);
|
|
||||||
|
|
||||||
p9_debug(P9_DEBUG_VFS, "filp: %p lock: %p\n", filp, fl);
|
|
||||||
|
|
||||||
/* No mandatory locks */
|
|
||||||
if (__mandatory_lock(inode) && fl->fl_type != F_UNLCK)
|
|
||||||
return -ENOLCK;
|
|
||||||
|
|
||||||
if ((IS_SETLK(cmd) || IS_SETLKW(cmd)) && fl->fl_type != F_UNLCK) {
|
|
||||||
filemap_write_and_wait(inode->i_mapping);
|
|
||||||
invalidate_mapping_pages(&inode->i_data, 0, -1);
|
|
||||||
}
|
|
||||||
|
|
||||||
return res;
|
|
||||||
}
|
|
||||||
|
|
||||||
static int v9fs_file_do_lock(struct file *filp, int cmd, struct file_lock *fl)
|
|
||||||
{
|
|
||||||
struct p9_flock flock;
|
|
||||||
struct p9_fid *fid;
|
|
||||||
uint8_t status = P9_LOCK_ERROR;
|
|
||||||
int res = 0;
|
|
||||||
unsigned char fl_type;
|
|
||||||
struct v9fs_session_info *v9ses;
|
|
||||||
|
|
||||||
fid = filp->private_data;
|
|
||||||
BUG_ON(fid == NULL);
|
|
||||||
|
|
||||||
if ((fl->fl_flags & FL_POSIX) != FL_POSIX)
|
|
||||||
BUG();
|
|
||||||
|
|
||||||
res = locks_lock_file_wait(filp, fl);
|
|
||||||
if (res < 0)
|
|
||||||
goto out;
|
|
||||||
|
|
||||||
/* convert posix lock to p9 tlock args */
|
|
||||||
memset(&flock, 0, sizeof(flock));
|
|
||||||
/* map the lock type */
|
|
||||||
switch (fl->fl_type) {
|
|
||||||
case F_RDLCK:
|
|
||||||
flock.type = P9_LOCK_TYPE_RDLCK;
|
|
||||||
break;
|
|
||||||
case F_WRLCK:
|
|
||||||
flock.type = P9_LOCK_TYPE_WRLCK;
|
|
||||||
break;
|
|
||||||
case F_UNLCK:
|
|
||||||
flock.type = P9_LOCK_TYPE_UNLCK;
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
flock.start = fl->fl_start;
|
|
||||||
if (fl->fl_end == OFFSET_MAX)
|
|
||||||
flock.length = 0;
|
|
||||||
else
|
|
||||||
flock.length = fl->fl_end - fl->fl_start + 1;
|
|
||||||
flock.proc_id = fl->fl_pid;
|
|
||||||
flock.client_id = fid->clnt->name;
|
|
||||||
if (IS_SETLKW(cmd))
|
|
||||||
flock.flags = P9_LOCK_FLAGS_BLOCK;
|
|
||||||
|
|
||||||
v9ses = v9fs_inode2v9ses(file_inode(filp));
|
|
||||||
|
|
||||||
/*
|
|
||||||
* if its a blocked request and we get P9_LOCK_BLOCKED as the status
|
|
||||||
* for lock request, keep on trying
|
|
||||||
*/
|
|
||||||
for (;;) {
|
|
||||||
res = p9_client_lock_dotl(fid, &flock, &status);
|
|
||||||
if (res < 0)
|
|
||||||
goto out_unlock;
|
|
||||||
|
|
||||||
if (status != P9_LOCK_BLOCKED)
|
|
||||||
break;
|
|
||||||
if (status == P9_LOCK_BLOCKED && !IS_SETLKW(cmd))
|
|
||||||
break;
|
|
||||||
if (schedule_timeout_interruptible(v9ses->session_lock_timeout)
|
|
||||||
!= 0)
|
|
||||||
break;
|
|
||||||
/*
|
|
||||||
* p9_client_lock_dotl overwrites flock.client_id with the
|
|
||||||
* server message, free and reuse the client name
|
|
||||||
*/
|
|
||||||
if (flock.client_id != fid->clnt->name) {
|
|
||||||
kfree(flock.client_id);
|
|
||||||
flock.client_id = fid->clnt->name;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/* map 9p status to VFS status */
|
|
||||||
switch (status) {
|
|
||||||
case P9_LOCK_SUCCESS:
|
|
||||||
res = 0;
|
|
||||||
break;
|
|
||||||
case P9_LOCK_BLOCKED:
|
|
||||||
res = -EAGAIN;
|
|
||||||
break;
|
|
||||||
default:
|
|
||||||
WARN_ONCE(1, "unknown lock status code: %d\n", status);
|
|
||||||
/* fallthough */
|
|
||||||
case P9_LOCK_ERROR:
|
|
||||||
case P9_LOCK_GRACE:
|
|
||||||
res = -ENOLCK;
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
|
|
||||||
out_unlock:
|
|
||||||
/*
|
|
||||||
* incase server returned error for lock request, revert
|
|
||||||
* it locally
|
|
||||||
*/
|
|
||||||
if (res < 0 && fl->fl_type != F_UNLCK) {
|
|
||||||
fl_type = fl->fl_type;
|
|
||||||
fl->fl_type = F_UNLCK;
|
|
||||||
/* Even if this fails we want to return the remote error */
|
|
||||||
locks_lock_file_wait(filp, fl);
|
|
||||||
fl->fl_type = fl_type;
|
|
||||||
}
|
|
||||||
if (flock.client_id != fid->clnt->name)
|
|
||||||
kfree(flock.client_id);
|
|
||||||
out:
|
|
||||||
return res;
|
|
||||||
}
|
|
||||||
|
|
||||||
static int v9fs_file_getlock(struct file *filp, struct file_lock *fl)
|
|
||||||
{
|
|
||||||
struct p9_getlock glock;
|
|
||||||
struct p9_fid *fid;
|
|
||||||
int res = 0;
|
|
||||||
|
|
||||||
fid = filp->private_data;
|
|
||||||
BUG_ON(fid == NULL);
|
|
||||||
|
|
||||||
posix_test_lock(filp, fl);
|
|
||||||
/*
|
|
||||||
* if we have a conflicting lock locally, no need to validate
|
|
||||||
* with server
|
|
||||||
*/
|
|
||||||
if (fl->fl_type != F_UNLCK)
|
|
||||||
return res;
|
|
||||||
|
|
||||||
/* convert posix lock to p9 tgetlock args */
|
|
||||||
memset(&glock, 0, sizeof(glock));
|
|
||||||
glock.type = P9_LOCK_TYPE_UNLCK;
|
|
||||||
glock.start = fl->fl_start;
|
|
||||||
if (fl->fl_end == OFFSET_MAX)
|
|
||||||
glock.length = 0;
|
|
||||||
else
|
|
||||||
glock.length = fl->fl_end - fl->fl_start + 1;
|
|
||||||
glock.proc_id = fl->fl_pid;
|
|
||||||
glock.client_id = fid->clnt->name;
|
|
||||||
|
|
||||||
res = p9_client_getlock_dotl(fid, &glock);
|
|
||||||
if (res < 0)
|
|
||||||
goto out;
|
|
||||||
/* map 9p lock type to os lock type */
|
|
||||||
switch (glock.type) {
|
|
||||||
case P9_LOCK_TYPE_RDLCK:
|
|
||||||
fl->fl_type = F_RDLCK;
|
|
||||||
break;
|
|
||||||
case P9_LOCK_TYPE_WRLCK:
|
|
||||||
fl->fl_type = F_WRLCK;
|
|
||||||
break;
|
|
||||||
case P9_LOCK_TYPE_UNLCK:
|
|
||||||
fl->fl_type = F_UNLCK;
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
if (glock.type != P9_LOCK_TYPE_UNLCK) {
|
|
||||||
fl->fl_start = glock.start;
|
|
||||||
if (glock.length == 0)
|
|
||||||
fl->fl_end = OFFSET_MAX;
|
|
||||||
else
|
|
||||||
fl->fl_end = glock.start + glock.length - 1;
|
|
||||||
fl->fl_pid = glock.proc_id;
|
|
||||||
}
|
|
||||||
out:
|
|
||||||
if (glock.client_id != fid->clnt->name)
|
|
||||||
kfree(glock.client_id);
|
|
||||||
return res;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* v9fs_file_lock_dotl - lock a file (or directory)
|
|
||||||
* @filp: file to be locked
|
|
||||||
* @cmd: lock command
|
|
||||||
* @fl: file lock structure
|
|
||||||
*
|
|
||||||
*/
|
|
||||||
|
|
||||||
static int v9fs_file_lock_dotl(struct file *filp, int cmd, struct file_lock *fl)
|
|
||||||
{
|
|
||||||
struct inode *inode = file_inode(filp);
|
|
||||||
int ret = -ENOLCK;
|
|
||||||
|
|
||||||
p9_debug(P9_DEBUG_VFS, "filp: %p cmd:%d lock: %p name: %pD\n",
|
|
||||||
filp, cmd, fl, filp);
|
|
||||||
|
|
||||||
/* No mandatory locks */
|
|
||||||
if (__mandatory_lock(inode) && fl->fl_type != F_UNLCK)
|
|
||||||
goto out_err;
|
|
||||||
|
|
||||||
if ((IS_SETLK(cmd) || IS_SETLKW(cmd)) && fl->fl_type != F_UNLCK) {
|
|
||||||
filemap_write_and_wait(inode->i_mapping);
|
|
||||||
invalidate_mapping_pages(&inode->i_data, 0, -1);
|
|
||||||
}
|
|
||||||
|
|
||||||
if (IS_SETLK(cmd) || IS_SETLKW(cmd))
|
|
||||||
ret = v9fs_file_do_lock(filp, cmd, fl);
|
|
||||||
else if (IS_GETLK(cmd))
|
|
||||||
ret = v9fs_file_getlock(filp, fl);
|
|
||||||
else
|
|
||||||
ret = -EINVAL;
|
|
||||||
out_err:
|
|
||||||
return ret;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* v9fs_file_flock_dotl - lock a file
|
|
||||||
* @filp: file to be locked
|
|
||||||
* @cmd: lock command
|
|
||||||
* @fl: file lock structure
|
|
||||||
*
|
|
||||||
*/
|
|
||||||
|
|
||||||
static int v9fs_file_flock_dotl(struct file *filp, int cmd,
|
|
||||||
struct file_lock *fl)
|
|
||||||
{
|
|
||||||
struct inode *inode = file_inode(filp);
|
|
||||||
int ret = -ENOLCK;
|
|
||||||
|
|
||||||
p9_debug(P9_DEBUG_VFS, "filp: %p cmd:%d lock: %p name: %pD\n",
|
|
||||||
filp, cmd, fl, filp);
|
|
||||||
|
|
||||||
/* No mandatory locks */
|
|
||||||
if (__mandatory_lock(inode) && fl->fl_type != F_UNLCK)
|
|
||||||
goto out_err;
|
|
||||||
|
|
||||||
if (!(fl->fl_flags & FL_FLOCK))
|
|
||||||
goto out_err;
|
|
||||||
|
|
||||||
if ((IS_SETLK(cmd) || IS_SETLKW(cmd)) && fl->fl_type != F_UNLCK) {
|
|
||||||
filemap_write_and_wait(inode->i_mapping);
|
|
||||||
invalidate_mapping_pages(&inode->i_data, 0, -1);
|
|
||||||
}
|
|
||||||
/* Convert flock to posix lock */
|
|
||||||
fl->fl_flags |= FL_POSIX;
|
|
||||||
fl->fl_flags ^= FL_FLOCK;
|
|
||||||
|
|
||||||
if (IS_SETLK(cmd) | IS_SETLKW(cmd))
|
|
||||||
ret = v9fs_file_do_lock(filp, cmd, fl);
|
|
||||||
else
|
|
||||||
ret = -EINVAL;
|
|
||||||
out_err:
|
|
||||||
return ret;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* v9fs_file_read - read from a file
|
|
||||||
* @filp: file pointer to read
|
|
||||||
* @udata: user data buffer to read data into
|
|
||||||
* @count: size of buffer
|
|
||||||
* @offset: offset at which to read data
|
|
||||||
*
|
|
||||||
*/
|
|
||||||
|
|
||||||
static ssize_t
|
|
||||||
v9fs_file_read_iter(struct kiocb *iocb, struct iov_iter *to)
|
|
||||||
{
|
|
||||||
struct p9_fid *fid = iocb->ki_filp->private_data;
|
|
||||||
int ret, err = 0;
|
|
||||||
|
|
||||||
p9_debug(P9_DEBUG_VFS, "count %zu offset %lld\n",
|
|
||||||
iov_iter_count(to), iocb->ki_pos);
|
|
||||||
|
|
||||||
ret = p9_client_read(fid, iocb->ki_pos, to, &err);
|
|
||||||
if (!ret)
|
|
||||||
return err;
|
|
||||||
|
|
||||||
iocb->ki_pos += ret;
|
|
||||||
return ret;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* v9fs_file_write - write to a file
|
|
||||||
* @filp: file pointer to write
|
|
||||||
* @data: data buffer to write data from
|
|
||||||
* @count: size of buffer
|
|
||||||
* @offset: offset at which to write data
|
|
||||||
*
|
|
||||||
*/
|
|
||||||
static ssize_t
|
|
||||||
v9fs_file_write_iter(struct kiocb *iocb, struct iov_iter *from)
|
|
||||||
{
|
|
||||||
struct file *file = iocb->ki_filp;
|
|
||||||
ssize_t retval;
|
|
||||||
loff_t origin;
|
|
||||||
int err = 0;
|
|
||||||
|
|
||||||
retval = generic_write_checks(iocb, from);
|
|
||||||
if (retval <= 0)
|
|
||||||
return retval;
|
|
||||||
|
|
||||||
origin = iocb->ki_pos;
|
|
||||||
retval = p9_client_write(file->private_data, iocb->ki_pos, from, &err);
|
|
||||||
if (retval > 0) {
|
|
||||||
struct inode *inode = file_inode(file);
|
|
||||||
loff_t i_size;
|
|
||||||
unsigned long pg_start, pg_end;
|
|
||||||
pg_start = origin >> PAGE_CACHE_SHIFT;
|
|
||||||
pg_end = (origin + retval - 1) >> PAGE_CACHE_SHIFT;
|
|
||||||
if (inode->i_mapping && inode->i_mapping->nrpages)
|
|
||||||
invalidate_inode_pages2_range(inode->i_mapping,
|
|
||||||
pg_start, pg_end);
|
|
||||||
iocb->ki_pos += retval;
|
|
||||||
i_size = i_size_read(inode);
|
|
||||||
if (iocb->ki_pos > i_size) {
|
|
||||||
inode_add_bytes(inode, iocb->ki_pos - i_size);
|
|
||||||
/*
|
|
||||||
* Need to serialize against i_size_write() in
|
|
||||||
* v9fs_stat2inode()
|
|
||||||
*/
|
|
||||||
v9fs_i_size_write(inode, iocb->ki_pos);
|
|
||||||
}
|
|
||||||
return retval;
|
|
||||||
}
|
|
||||||
return err;
|
|
||||||
}
|
|
||||||
|
|
||||||
static int v9fs_file_fsync(struct file *filp, loff_t start, loff_t end,
|
|
||||||
int datasync)
|
|
||||||
{
|
|
||||||
struct p9_fid *fid;
|
|
||||||
struct inode *inode = filp->f_mapping->host;
|
|
||||||
struct p9_wstat wstat;
|
|
||||||
int retval;
|
|
||||||
|
|
||||||
retval = filemap_write_and_wait_range(inode->i_mapping, start, end);
|
|
||||||
if (retval)
|
|
||||||
return retval;
|
|
||||||
|
|
||||||
mutex_lock(&inode->i_mutex);
|
|
||||||
p9_debug(P9_DEBUG_VFS, "filp %p datasync %x\n", filp, datasync);
|
|
||||||
|
|
||||||
fid = filp->private_data;
|
|
||||||
v9fs_blank_wstat(&wstat);
|
|
||||||
|
|
||||||
retval = p9_client_wstat(fid, &wstat);
|
|
||||||
mutex_unlock(&inode->i_mutex);
|
|
||||||
|
|
||||||
return retval;
|
|
||||||
}
|
|
||||||
|
|
||||||
int v9fs_file_fsync_dotl(struct file *filp, loff_t start, loff_t end,
|
|
||||||
int datasync)
|
|
||||||
{
|
|
||||||
struct p9_fid *fid;
|
|
||||||
struct inode *inode = filp->f_mapping->host;
|
|
||||||
int retval;
|
|
||||||
|
|
||||||
retval = filemap_write_and_wait_range(inode->i_mapping, start, end);
|
|
||||||
if (retval)
|
|
||||||
return retval;
|
|
||||||
|
|
||||||
mutex_lock(&inode->i_mutex);
|
|
||||||
p9_debug(P9_DEBUG_VFS, "filp %p datasync %x\n", filp, datasync);
|
|
||||||
|
|
||||||
fid = filp->private_data;
|
|
||||||
|
|
||||||
retval = p9_client_fsync(fid, datasync);
|
|
||||||
mutex_unlock(&inode->i_mutex);
|
|
||||||
|
|
||||||
return retval;
|
|
||||||
}
|
|
||||||
|
|
||||||
static int
|
|
||||||
v9fs_file_mmap(struct file *filp, struct vm_area_struct *vma)
|
|
||||||
{
|
|
||||||
int retval;
|
|
||||||
|
|
||||||
|
|
||||||
retval = generic_file_mmap(filp, vma);
|
|
||||||
if (!retval)
|
|
||||||
vma->vm_ops = &v9fs_file_vm_ops;
|
|
||||||
|
|
||||||
return retval;
|
|
||||||
}
|
|
||||||
|
|
||||||
static int
|
|
||||||
v9fs_mmap_file_mmap(struct file *filp, struct vm_area_struct *vma)
|
|
||||||
{
|
|
||||||
int retval;
|
|
||||||
struct inode *inode;
|
|
||||||
struct v9fs_inode *v9inode;
|
|
||||||
struct p9_fid *fid;
|
|
||||||
|
|
||||||
inode = file_inode(filp);
|
|
||||||
v9inode = V9FS_I(inode);
|
|
||||||
mutex_lock(&v9inode->v_mutex);
|
|
||||||
if (!v9inode->writeback_fid &&
|
|
||||||
(vma->vm_flags & VM_WRITE)) {
|
|
||||||
/*
|
|
||||||
* clone a fid and add it to writeback_fid
|
|
||||||
* we do it during mmap instead of
|
|
||||||
* page dirty time via write_begin/page_mkwrite
|
|
||||||
* because we want write after unlink usecase
|
|
||||||
* to work.
|
|
||||||
*/
|
|
||||||
fid = v9fs_writeback_fid(file_dentry(filp));
|
|
||||||
if (IS_ERR(fid)) {
|
|
||||||
retval = PTR_ERR(fid);
|
|
||||||
mutex_unlock(&v9inode->v_mutex);
|
|
||||||
return retval;
|
|
||||||
}
|
|
||||||
v9inode->writeback_fid = (void *) fid;
|
|
||||||
}
|
|
||||||
mutex_unlock(&v9inode->v_mutex);
|
|
||||||
|
|
||||||
retval = generic_file_mmap(filp, vma);
|
|
||||||
if (!retval)
|
|
||||||
vma->vm_ops = &v9fs_mmap_file_vm_ops;
|
|
||||||
|
|
||||||
return retval;
|
|
||||||
}
|
|
||||||
|
|
||||||
static int
|
|
||||||
v9fs_vm_page_mkwrite(struct vm_area_struct *vma, struct vm_fault *vmf)
|
|
||||||
{
|
|
||||||
struct v9fs_inode *v9inode;
|
|
||||||
struct page *page = vmf->page;
|
|
||||||
struct file *filp = vma->vm_file;
|
|
||||||
struct inode *inode = file_inode(filp);
|
|
||||||
|
|
||||||
|
|
||||||
p9_debug(P9_DEBUG_VFS, "page %p fid %lx\n",
|
|
||||||
page, (unsigned long)filp->private_data);
|
|
||||||
|
|
||||||
/* Update file times before taking page lock */
|
|
||||||
file_update_time(filp);
|
|
||||||
|
|
||||||
v9inode = V9FS_I(inode);
|
|
||||||
/* make sure the cache has finished storing the page */
|
|
||||||
v9fs_fscache_wait_on_page_write(inode, page);
|
|
||||||
BUG_ON(!v9inode->writeback_fid);
|
|
||||||
lock_page(page);
|
|
||||||
if (page->mapping != inode->i_mapping)
|
|
||||||
goto out_unlock;
|
|
||||||
wait_for_stable_page(page);
|
|
||||||
|
|
||||||
return VM_FAULT_LOCKED;
|
|
||||||
out_unlock:
|
|
||||||
unlock_page(page);
|
|
||||||
return VM_FAULT_NOPAGE;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* v9fs_mmap_file_read - read from a file
|
|
||||||
* @filp: file pointer to read
|
|
||||||
* @data: user data buffer to read data into
|
|
||||||
* @count: size of buffer
|
|
||||||
* @offset: offset at which to read data
|
|
||||||
*
|
|
||||||
*/
|
|
||||||
static ssize_t
|
|
||||||
v9fs_mmap_file_read_iter(struct kiocb *iocb, struct iov_iter *to)
|
|
||||||
{
|
|
||||||
/* TODO: Check if there are dirty pages */
|
|
||||||
return v9fs_file_read_iter(iocb, to);
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* v9fs_mmap_file_write - write to a file
|
|
||||||
* @filp: file pointer to write
|
|
||||||
* @data: data buffer to write data from
|
|
||||||
* @count: size of buffer
|
|
||||||
* @offset: offset at which to write data
|
|
||||||
*
|
|
||||||
*/
|
|
||||||
static ssize_t
|
|
||||||
v9fs_mmap_file_write_iter(struct kiocb *iocb, struct iov_iter *from)
|
|
||||||
{
|
|
||||||
/*
|
|
||||||
* TODO: invalidate mmaps on filp's inode between
|
|
||||||
* offset and offset+count
|
|
||||||
*/
|
|
||||||
return v9fs_file_write_iter(iocb, from);
|
|
||||||
}
|
|
||||||
|
|
||||||
static void v9fs_mmap_vm_close(struct vm_area_struct *vma)
|
|
||||||
{
|
|
||||||
struct inode *inode;
|
|
||||||
|
|
||||||
struct writeback_control wbc = {
|
|
||||||
.nr_to_write = LONG_MAX,
|
|
||||||
.sync_mode = WB_SYNC_ALL,
|
|
||||||
.range_start = vma->vm_pgoff * PAGE_SIZE,
|
|
||||||
/* absolute end, byte at end included */
|
|
||||||
.range_end = vma->vm_pgoff * PAGE_SIZE +
|
|
||||||
(vma->vm_end - vma->vm_start - 1),
|
|
||||||
};
|
|
||||||
|
|
||||||
|
|
||||||
p9_debug(P9_DEBUG_VFS, "9p VMA close, %p, flushing", vma);
|
|
||||||
|
|
||||||
inode = file_inode(vma->vm_file);
|
|
||||||
|
|
||||||
if (!mapping_cap_writeback_dirty(inode->i_mapping))
|
|
||||||
wbc.nr_to_write = 0;
|
|
||||||
|
|
||||||
might_sleep();
|
|
||||||
sync_inode(inode, &wbc);
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
static const struct vm_operations_struct v9fs_file_vm_ops = {
|
|
||||||
.fault = filemap_fault,
|
|
||||||
.map_pages = filemap_map_pages,
|
|
||||||
.page_mkwrite = v9fs_vm_page_mkwrite,
|
|
||||||
};
|
|
||||||
|
|
||||||
static const struct vm_operations_struct v9fs_mmap_file_vm_ops = {
|
|
||||||
.close = v9fs_mmap_vm_close,
|
|
||||||
.fault = filemap_fault,
|
|
||||||
.map_pages = filemap_map_pages,
|
|
||||||
.page_mkwrite = v9fs_vm_page_mkwrite,
|
|
||||||
};
|
|
||||||
|
|
||||||
|
|
||||||
const struct file_operations v9fs_cached_file_operations = {
|
|
||||||
.llseek = generic_file_llseek,
|
|
||||||
.read_iter = generic_file_read_iter,
|
|
||||||
.write_iter = generic_file_write_iter,
|
|
||||||
.open = v9fs_file_open,
|
|
||||||
.release = v9fs_dir_release,
|
|
||||||
.lock = v9fs_file_lock,
|
|
||||||
.mmap = v9fs_file_mmap,
|
|
||||||
.fsync = v9fs_file_fsync,
|
|
||||||
};
|
|
||||||
|
|
||||||
const struct file_operations v9fs_cached_file_operations_dotl = {
|
|
||||||
.llseek = generic_file_llseek,
|
|
||||||
.read_iter = generic_file_read_iter,
|
|
||||||
.write_iter = generic_file_write_iter,
|
|
||||||
.open = v9fs_file_open,
|
|
||||||
.release = v9fs_dir_release,
|
|
||||||
.lock = v9fs_file_lock_dotl,
|
|
||||||
.flock = v9fs_file_flock_dotl,
|
|
||||||
.mmap = v9fs_file_mmap,
|
|
||||||
.fsync = v9fs_file_fsync_dotl,
|
|
||||||
};
|
|
||||||
|
|
||||||
const struct file_operations v9fs_file_operations = {
|
|
||||||
.llseek = generic_file_llseek,
|
|
||||||
.read_iter = v9fs_file_read_iter,
|
|
||||||
.write_iter = v9fs_file_write_iter,
|
|
||||||
.open = v9fs_file_open,
|
|
||||||
.release = v9fs_dir_release,
|
|
||||||
.lock = v9fs_file_lock,
|
|
||||||
.mmap = generic_file_readonly_mmap,
|
|
||||||
.fsync = v9fs_file_fsync,
|
|
||||||
};
|
|
||||||
|
|
||||||
const struct file_operations v9fs_file_operations_dotl = {
|
|
||||||
.llseek = generic_file_llseek,
|
|
||||||
.read_iter = v9fs_file_read_iter,
|
|
||||||
.write_iter = v9fs_file_write_iter,
|
|
||||||
.open = v9fs_file_open,
|
|
||||||
.release = v9fs_dir_release,
|
|
||||||
.lock = v9fs_file_lock_dotl,
|
|
||||||
.flock = v9fs_file_flock_dotl,
|
|
||||||
.mmap = generic_file_readonly_mmap,
|
|
||||||
.fsync = v9fs_file_fsync_dotl,
|
|
||||||
};
|
|
||||||
|
|
||||||
const struct file_operations v9fs_mmap_file_operations = {
|
|
||||||
.llseek = generic_file_llseek,
|
|
||||||
.read_iter = v9fs_mmap_file_read_iter,
|
|
||||||
.write_iter = v9fs_mmap_file_write_iter,
|
|
||||||
.open = v9fs_file_open,
|
|
||||||
.release = v9fs_dir_release,
|
|
||||||
.lock = v9fs_file_lock,
|
|
||||||
.mmap = v9fs_mmap_file_mmap,
|
|
||||||
.fsync = v9fs_file_fsync,
|
|
||||||
};
|
|
||||||
|
|
||||||
const struct file_operations v9fs_mmap_file_operations_dotl = {
|
|
||||||
.llseek = generic_file_llseek,
|
|
||||||
.read_iter = v9fs_mmap_file_read_iter,
|
|
||||||
.write_iter = v9fs_mmap_file_write_iter,
|
|
||||||
.open = v9fs_file_open,
|
|
||||||
.release = v9fs_dir_release,
|
|
||||||
.lock = v9fs_file_lock_dotl,
|
|
||||||
.flock = v9fs_file_flock_dotl,
|
|
||||||
.mmap = v9fs_mmap_file_mmap,
|
|
||||||
.fsync = v9fs_file_fsync_dotl,
|
|
||||||
};
|
|
File diff suppressed because it is too large
Load Diff
@ -1,999 +0,0 @@
|
|||||||
/*
|
|
||||||
* linux/fs/9p/vfs_inode_dotl.c
|
|
||||||
*
|
|
||||||
* This file contains vfs inode ops for the 9P2000.L protocol.
|
|
||||||
*
|
|
||||||
* Copyright (C) 2004 by Eric Van Hensbergen <ericvh@gmail.com>
|
|
||||||
* Copyright (C) 2002 by Ron Minnich <rminnich@lanl.gov>
|
|
||||||
*
|
|
||||||
* This program is free software; you can redistribute it and/or modify
|
|
||||||
* it under the terms of the GNU General Public License version 2
|
|
||||||
* as published by the Free Software Foundation.
|
|
||||||
*
|
|
||||||
* This program is distributed in the hope that it will be useful,
|
|
||||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
||||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
||||||
* GNU General Public License for more details.
|
|
||||||
*
|
|
||||||
* You should have received a copy of the GNU General Public License
|
|
||||||
* along with this program; if not, write to:
|
|
||||||
* Free Software Foundation
|
|
||||||
* 51 Franklin Street, Fifth Floor
|
|
||||||
* Boston, MA 02111-1301 USA
|
|
||||||
*
|
|
||||||
*/
|
|
||||||
|
|
||||||
#include <linux/module.h>
|
|
||||||
#include <linux/errno.h>
|
|
||||||
#include <linux/fs.h>
|
|
||||||
#include <linux/file.h>
|
|
||||||
#include <linux/pagemap.h>
|
|
||||||
#include <linux/stat.h>
|
|
||||||
#include <linux/string.h>
|
|
||||||
#include <linux/inet.h>
|
|
||||||
#include <linux/namei.h>
|
|
||||||
#include <linux/idr.h>
|
|
||||||
#include <linux/sched.h>
|
|
||||||
#include <linux/slab.h>
|
|
||||||
#include <linux/xattr.h>
|
|
||||||
#include <linux/posix_acl.h>
|
|
||||||
#include <net/9p/9p.h>
|
|
||||||
#include <net/9p/client.h>
|
|
||||||
|
|
||||||
#include "v9fs.h"
|
|
||||||
#include "v9fs_vfs.h"
|
|
||||||
#include "fid.h"
|
|
||||||
#include "cache.h"
|
|
||||||
#include "xattr.h"
|
|
||||||
#include "acl.h"
|
|
||||||
|
|
||||||
static int
|
|
||||||
v9fs_vfs_mknod_dotl(struct inode *dir, struct dentry *dentry, umode_t omode,
|
|
||||||
dev_t rdev);
|
|
||||||
|
|
||||||
/**
|
|
||||||
* v9fs_get_fsgid_for_create - Helper function to get the gid for creating a
|
|
||||||
* new file system object. This checks the S_ISGID to determine the owning
|
|
||||||
* group of the new file system object.
|
|
||||||
*/
|
|
||||||
|
|
||||||
static kgid_t v9fs_get_fsgid_for_create(struct inode *dir_inode)
|
|
||||||
{
|
|
||||||
BUG_ON(dir_inode == NULL);
|
|
||||||
|
|
||||||
if (dir_inode->i_mode & S_ISGID) {
|
|
||||||
/* set_gid bit is set.*/
|
|
||||||
return dir_inode->i_gid;
|
|
||||||
}
|
|
||||||
return current_fsgid();
|
|
||||||
}
|
|
||||||
|
|
||||||
static int v9fs_test_inode_dotl(struct inode *inode, void *data)
|
|
||||||
{
|
|
||||||
struct v9fs_inode *v9inode = V9FS_I(inode);
|
|
||||||
struct p9_stat_dotl *st = (struct p9_stat_dotl *)data;
|
|
||||||
|
|
||||||
/* don't match inode of different type */
|
|
||||||
if ((inode->i_mode & S_IFMT) != (st->st_mode & S_IFMT))
|
|
||||||
return 0;
|
|
||||||
|
|
||||||
if (inode->i_generation != st->st_gen)
|
|
||||||
return 0;
|
|
||||||
|
|
||||||
/* compare qid details */
|
|
||||||
if (memcmp(&v9inode->qid.version,
|
|
||||||
&st->qid.version, sizeof(v9inode->qid.version)))
|
|
||||||
return 0;
|
|
||||||
|
|
||||||
if (v9inode->qid.type != st->qid.type)
|
|
||||||
return 0;
|
|
||||||
|
|
||||||
if (v9inode->qid.path != st->qid.path)
|
|
||||||
return 0;
|
|
||||||
return 1;
|
|
||||||
}
|
|
||||||
|
|
||||||
/* Always get a new inode */
|
|
||||||
static int v9fs_test_new_inode_dotl(struct inode *inode, void *data)
|
|
||||||
{
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
static int v9fs_set_inode_dotl(struct inode *inode, void *data)
|
|
||||||
{
|
|
||||||
struct v9fs_inode *v9inode = V9FS_I(inode);
|
|
||||||
struct p9_stat_dotl *st = (struct p9_stat_dotl *)data;
|
|
||||||
|
|
||||||
memcpy(&v9inode->qid, &st->qid, sizeof(st->qid));
|
|
||||||
inode->i_generation = st->st_gen;
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
static struct inode *v9fs_qid_iget_dotl(struct super_block *sb,
|
|
||||||
struct p9_qid *qid,
|
|
||||||
struct p9_fid *fid,
|
|
||||||
struct p9_stat_dotl *st,
|
|
||||||
int new)
|
|
||||||
{
|
|
||||||
int retval;
|
|
||||||
unsigned long i_ino;
|
|
||||||
struct inode *inode;
|
|
||||||
struct v9fs_session_info *v9ses = sb->s_fs_info;
|
|
||||||
int (*test)(struct inode *, void *);
|
|
||||||
|
|
||||||
if (new)
|
|
||||||
test = v9fs_test_new_inode_dotl;
|
|
||||||
else
|
|
||||||
test = v9fs_test_inode_dotl;
|
|
||||||
|
|
||||||
i_ino = v9fs_qid2ino(qid);
|
|
||||||
inode = iget5_locked(sb, i_ino, test, v9fs_set_inode_dotl, st);
|
|
||||||
if (!inode)
|
|
||||||
return ERR_PTR(-ENOMEM);
|
|
||||||
if (!(inode->i_state & I_NEW))
|
|
||||||
return inode;
|
|
||||||
/*
|
|
||||||
* initialize the inode with the stat info
|
|
||||||
* FIXME!! we may need support for stale inodes
|
|
||||||
* later.
|
|
||||||
*/
|
|
||||||
inode->i_ino = i_ino;
|
|
||||||
retval = v9fs_init_inode(v9ses, inode,
|
|
||||||
st->st_mode, new_decode_dev(st->st_rdev));
|
|
||||||
if (retval)
|
|
||||||
goto error;
|
|
||||||
|
|
||||||
v9fs_stat2inode_dotl(st, inode, 0);
|
|
||||||
v9fs_cache_inode_get_cookie(inode);
|
|
||||||
retval = v9fs_get_acl(inode, fid);
|
|
||||||
if (retval)
|
|
||||||
goto error;
|
|
||||||
|
|
||||||
unlock_new_inode(inode);
|
|
||||||
return inode;
|
|
||||||
error:
|
|
||||||
iget_failed(inode);
|
|
||||||
return ERR_PTR(retval);
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
struct inode *
|
|
||||||
v9fs_inode_from_fid_dotl(struct v9fs_session_info *v9ses, struct p9_fid *fid,
|
|
||||||
struct super_block *sb, int new)
|
|
||||||
{
|
|
||||||
struct p9_stat_dotl *st;
|
|
||||||
struct inode *inode = NULL;
|
|
||||||
|
|
||||||
st = p9_client_getattr_dotl(fid, P9_STATS_BASIC | P9_STATS_GEN);
|
|
||||||
if (IS_ERR(st))
|
|
||||||
return ERR_CAST(st);
|
|
||||||
|
|
||||||
inode = v9fs_qid_iget_dotl(sb, &st->qid, fid, st, new);
|
|
||||||
kfree(st);
|
|
||||||
return inode;
|
|
||||||
}
|
|
||||||
|
|
||||||
struct dotl_openflag_map {
|
|
||||||
int open_flag;
|
|
||||||
int dotl_flag;
|
|
||||||
};
|
|
||||||
|
|
||||||
static int v9fs_mapped_dotl_flags(int flags)
|
|
||||||
{
|
|
||||||
int i;
|
|
||||||
int rflags = 0;
|
|
||||||
struct dotl_openflag_map dotl_oflag_map[] = {
|
|
||||||
{ O_CREAT, P9_DOTL_CREATE },
|
|
||||||
{ O_EXCL, P9_DOTL_EXCL },
|
|
||||||
{ O_NOCTTY, P9_DOTL_NOCTTY },
|
|
||||||
{ O_APPEND, P9_DOTL_APPEND },
|
|
||||||
{ O_NONBLOCK, P9_DOTL_NONBLOCK },
|
|
||||||
{ O_DSYNC, P9_DOTL_DSYNC },
|
|
||||||
{ FASYNC, P9_DOTL_FASYNC },
|
|
||||||
{ O_DIRECT, P9_DOTL_DIRECT },
|
|
||||||
{ O_LARGEFILE, P9_DOTL_LARGEFILE },
|
|
||||||
{ O_DIRECTORY, P9_DOTL_DIRECTORY },
|
|
||||||
{ O_NOFOLLOW, P9_DOTL_NOFOLLOW },
|
|
||||||
{ O_NOATIME, P9_DOTL_NOATIME },
|
|
||||||
{ O_CLOEXEC, P9_DOTL_CLOEXEC },
|
|
||||||
{ O_SYNC, P9_DOTL_SYNC},
|
|
||||||
};
|
|
||||||
for (i = 0; i < ARRAY_SIZE(dotl_oflag_map); i++) {
|
|
||||||
if (flags & dotl_oflag_map[i].open_flag)
|
|
||||||
rflags |= dotl_oflag_map[i].dotl_flag;
|
|
||||||
}
|
|
||||||
return rflags;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* v9fs_open_to_dotl_flags- convert Linux specific open flags to
|
|
||||||
* plan 9 open flag.
|
|
||||||
* @flags: flags to convert
|
|
||||||
*/
|
|
||||||
int v9fs_open_to_dotl_flags(int flags)
|
|
||||||
{
|
|
||||||
int rflags = 0;
|
|
||||||
|
|
||||||
/*
|
|
||||||
* We have same bits for P9_DOTL_READONLY, P9_DOTL_WRONLY
|
|
||||||
* and P9_DOTL_NOACCESS
|
|
||||||
*/
|
|
||||||
rflags |= flags & O_ACCMODE;
|
|
||||||
rflags |= v9fs_mapped_dotl_flags(flags);
|
|
||||||
|
|
||||||
return rflags;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* v9fs_vfs_create_dotl - VFS hook to create files for 9P2000.L protocol.
|
|
||||||
* @dir: directory inode that is being created
|
|
||||||
* @dentry: dentry that is being deleted
|
|
||||||
* @omode: create permissions
|
|
||||||
*
|
|
||||||
*/
|
|
||||||
|
|
||||||
static int
|
|
||||||
v9fs_vfs_create_dotl(struct inode *dir, struct dentry *dentry, umode_t omode,
|
|
||||||
bool excl)
|
|
||||||
{
|
|
||||||
return v9fs_vfs_mknod_dotl(dir, dentry, omode, 0);
|
|
||||||
}
|
|
||||||
|
|
||||||
static int
|
|
||||||
v9fs_vfs_atomic_open_dotl(struct inode *dir, struct dentry *dentry,
|
|
||||||
struct file *file, unsigned flags, umode_t omode,
|
|
||||||
int *opened)
|
|
||||||
{
|
|
||||||
int err = 0;
|
|
||||||
kgid_t gid;
|
|
||||||
umode_t mode;
|
|
||||||
char *name = NULL;
|
|
||||||
struct p9_qid qid;
|
|
||||||
struct inode *inode;
|
|
||||||
struct p9_fid *fid = NULL;
|
|
||||||
struct v9fs_inode *v9inode;
|
|
||||||
struct p9_fid *dfid, *ofid, *inode_fid;
|
|
||||||
struct v9fs_session_info *v9ses;
|
|
||||||
struct posix_acl *pacl = NULL, *dacl = NULL;
|
|
||||||
struct dentry *res = NULL;
|
|
||||||
|
|
||||||
if (d_unhashed(dentry)) {
|
|
||||||
res = v9fs_vfs_lookup(dir, dentry, 0);
|
|
||||||
if (IS_ERR(res))
|
|
||||||
return PTR_ERR(res);
|
|
||||||
|
|
||||||
if (res)
|
|
||||||
dentry = res;
|
|
||||||
}
|
|
||||||
|
|
||||||
/* Only creates */
|
|
||||||
if (!(flags & O_CREAT) || d_really_is_positive(dentry))
|
|
||||||
return finish_no_open(file, res);
|
|
||||||
|
|
||||||
v9ses = v9fs_inode2v9ses(dir);
|
|
||||||
|
|
||||||
name = (char *) dentry->d_name.name;
|
|
||||||
p9_debug(P9_DEBUG_VFS, "name:%s flags:0x%x mode:0x%hx\n",
|
|
||||||
name, flags, omode);
|
|
||||||
|
|
||||||
dfid = v9fs_fid_lookup(dentry->d_parent);
|
|
||||||
if (IS_ERR(dfid)) {
|
|
||||||
err = PTR_ERR(dfid);
|
|
||||||
p9_debug(P9_DEBUG_VFS, "fid lookup failed %d\n", err);
|
|
||||||
goto out;
|
|
||||||
}
|
|
||||||
|
|
||||||
/* clone a fid to use for creation */
|
|
||||||
ofid = p9_client_walk(dfid, 0, NULL, 1);
|
|
||||||
if (IS_ERR(ofid)) {
|
|
||||||
err = PTR_ERR(ofid);
|
|
||||||
p9_debug(P9_DEBUG_VFS, "p9_client_walk failed %d\n", err);
|
|
||||||
goto out;
|
|
||||||
}
|
|
||||||
|
|
||||||
gid = v9fs_get_fsgid_for_create(dir);
|
|
||||||
|
|
||||||
mode = omode;
|
|
||||||
/* Update mode based on ACL value */
|
|
||||||
err = v9fs_acl_mode(dir, &mode, &dacl, &pacl);
|
|
||||||
if (err) {
|
|
||||||
p9_debug(P9_DEBUG_VFS, "Failed to get acl values in creat %d\n",
|
|
||||||
err);
|
|
||||||
goto error;
|
|
||||||
}
|
|
||||||
err = p9_client_create_dotl(ofid, name, v9fs_open_to_dotl_flags(flags),
|
|
||||||
mode, gid, &qid);
|
|
||||||
if (err < 0) {
|
|
||||||
p9_debug(P9_DEBUG_VFS, "p9_client_open_dotl failed in creat %d\n",
|
|
||||||
err);
|
|
||||||
goto error;
|
|
||||||
}
|
|
||||||
v9fs_invalidate_inode_attr(dir);
|
|
||||||
|
|
||||||
/* instantiate inode and assign the unopened fid to the dentry */
|
|
||||||
fid = p9_client_walk(dfid, 1, &name, 1);
|
|
||||||
if (IS_ERR(fid)) {
|
|
||||||
err = PTR_ERR(fid);
|
|
||||||
p9_debug(P9_DEBUG_VFS, "p9_client_walk failed %d\n", err);
|
|
||||||
fid = NULL;
|
|
||||||
goto error;
|
|
||||||
}
|
|
||||||
inode = v9fs_get_new_inode_from_fid(v9ses, fid, dir->i_sb);
|
|
||||||
if (IS_ERR(inode)) {
|
|
||||||
err = PTR_ERR(inode);
|
|
||||||
p9_debug(P9_DEBUG_VFS, "inode creation failed %d\n", err);
|
|
||||||
goto error;
|
|
||||||
}
|
|
||||||
/* Now set the ACL based on the default value */
|
|
||||||
v9fs_set_create_acl(inode, fid, dacl, pacl);
|
|
||||||
|
|
||||||
v9fs_fid_add(dentry, fid);
|
|
||||||
d_instantiate(dentry, inode);
|
|
||||||
|
|
||||||
v9inode = V9FS_I(inode);
|
|
||||||
mutex_lock(&v9inode->v_mutex);
|
|
||||||
if ((v9ses->cache == CACHE_LOOSE || v9ses->cache == CACHE_FSCACHE) &&
|
|
||||||
!v9inode->writeback_fid &&
|
|
||||||
((flags & O_ACCMODE) != O_RDONLY)) {
|
|
||||||
/*
|
|
||||||
* clone a fid and add it to writeback_fid
|
|
||||||
* we do it during open time instead of
|
|
||||||
* page dirty time via write_begin/page_mkwrite
|
|
||||||
* because we want write after unlink usecase
|
|
||||||
* to work.
|
|
||||||
*/
|
|
||||||
inode_fid = v9fs_writeback_fid(dentry);
|
|
||||||
if (IS_ERR(inode_fid)) {
|
|
||||||
err = PTR_ERR(inode_fid);
|
|
||||||
mutex_unlock(&v9inode->v_mutex);
|
|
||||||
goto err_clunk_old_fid;
|
|
||||||
}
|
|
||||||
v9inode->writeback_fid = (void *) inode_fid;
|
|
||||||
}
|
|
||||||
mutex_unlock(&v9inode->v_mutex);
|
|
||||||
/* Since we are opening a file, assign the open fid to the file */
|
|
||||||
err = finish_open(file, dentry, generic_file_open, opened);
|
|
||||||
if (err)
|
|
||||||
goto err_clunk_old_fid;
|
|
||||||
file->private_data = ofid;
|
|
||||||
if (v9ses->cache == CACHE_LOOSE || v9ses->cache == CACHE_FSCACHE)
|
|
||||||
v9fs_cache_inode_set_cookie(inode, file);
|
|
||||||
*opened |= FILE_CREATED;
|
|
||||||
out:
|
|
||||||
v9fs_put_acl(dacl, pacl);
|
|
||||||
dput(res);
|
|
||||||
return err;
|
|
||||||
|
|
||||||
error:
|
|
||||||
if (fid)
|
|
||||||
p9_client_clunk(fid);
|
|
||||||
err_clunk_old_fid:
|
|
||||||
if (ofid)
|
|
||||||
p9_client_clunk(ofid);
|
|
||||||
goto out;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* v9fs_vfs_mkdir_dotl - VFS mkdir hook to create a directory
|
|
||||||
* @dir: inode that is being unlinked
|
|
||||||
* @dentry: dentry that is being unlinked
|
|
||||||
* @omode: mode for new directory
|
|
||||||
*
|
|
||||||
*/
|
|
||||||
|
|
||||||
static int v9fs_vfs_mkdir_dotl(struct inode *dir,
|
|
||||||
struct dentry *dentry, umode_t omode)
|
|
||||||
{
|
|
||||||
int err;
|
|
||||||
struct v9fs_session_info *v9ses;
|
|
||||||
struct p9_fid *fid = NULL, *dfid = NULL;
|
|
||||||
kgid_t gid;
|
|
||||||
char *name;
|
|
||||||
umode_t mode;
|
|
||||||
struct inode *inode;
|
|
||||||
struct p9_qid qid;
|
|
||||||
struct dentry *dir_dentry;
|
|
||||||
struct posix_acl *dacl = NULL, *pacl = NULL;
|
|
||||||
|
|
||||||
p9_debug(P9_DEBUG_VFS, "name %pd\n", dentry);
|
|
||||||
err = 0;
|
|
||||||
v9ses = v9fs_inode2v9ses(dir);
|
|
||||||
|
|
||||||
omode |= S_IFDIR;
|
|
||||||
if (dir->i_mode & S_ISGID)
|
|
||||||
omode |= S_ISGID;
|
|
||||||
|
|
||||||
dir_dentry = dentry->d_parent;
|
|
||||||
dfid = v9fs_fid_lookup(dir_dentry);
|
|
||||||
if (IS_ERR(dfid)) {
|
|
||||||
err = PTR_ERR(dfid);
|
|
||||||
p9_debug(P9_DEBUG_VFS, "fid lookup failed %d\n", err);
|
|
||||||
dfid = NULL;
|
|
||||||
goto error;
|
|
||||||
}
|
|
||||||
|
|
||||||
gid = v9fs_get_fsgid_for_create(dir);
|
|
||||||
mode = omode;
|
|
||||||
/* Update mode based on ACL value */
|
|
||||||
err = v9fs_acl_mode(dir, &mode, &dacl, &pacl);
|
|
||||||
if (err) {
|
|
||||||
p9_debug(P9_DEBUG_VFS, "Failed to get acl values in mkdir %d\n",
|
|
||||||
err);
|
|
||||||
goto error;
|
|
||||||
}
|
|
||||||
name = (char *) dentry->d_name.name;
|
|
||||||
err = p9_client_mkdir_dotl(dfid, name, mode, gid, &qid);
|
|
||||||
if (err < 0)
|
|
||||||
goto error;
|
|
||||||
|
|
||||||
fid = p9_client_walk(dfid, 1, &name, 1);
|
|
||||||
if (IS_ERR(fid)) {
|
|
||||||
err = PTR_ERR(fid);
|
|
||||||
p9_debug(P9_DEBUG_VFS, "p9_client_walk failed %d\n",
|
|
||||||
err);
|
|
||||||
fid = NULL;
|
|
||||||
goto error;
|
|
||||||
}
|
|
||||||
|
|
||||||
/* instantiate inode and assign the unopened fid to the dentry */
|
|
||||||
if (v9ses->cache == CACHE_LOOSE || v9ses->cache == CACHE_FSCACHE) {
|
|
||||||
inode = v9fs_get_new_inode_from_fid(v9ses, fid, dir->i_sb);
|
|
||||||
if (IS_ERR(inode)) {
|
|
||||||
err = PTR_ERR(inode);
|
|
||||||
p9_debug(P9_DEBUG_VFS, "inode creation failed %d\n",
|
|
||||||
err);
|
|
||||||
goto error;
|
|
||||||
}
|
|
||||||
v9fs_fid_add(dentry, fid);
|
|
||||||
v9fs_set_create_acl(inode, fid, dacl, pacl);
|
|
||||||
d_instantiate(dentry, inode);
|
|
||||||
fid = NULL;
|
|
||||||
err = 0;
|
|
||||||
} else {
|
|
||||||
/*
|
|
||||||
* Not in cached mode. No need to populate
|
|
||||||
* inode with stat. We need to get an inode
|
|
||||||
* so that we can set the acl with dentry
|
|
||||||
*/
|
|
||||||
inode = v9fs_get_inode(dir->i_sb, mode, 0);
|
|
||||||
if (IS_ERR(inode)) {
|
|
||||||
err = PTR_ERR(inode);
|
|
||||||
goto error;
|
|
||||||
}
|
|
||||||
v9fs_set_create_acl(inode, fid, dacl, pacl);
|
|
||||||
d_instantiate(dentry, inode);
|
|
||||||
}
|
|
||||||
inc_nlink(dir);
|
|
||||||
v9fs_invalidate_inode_attr(dir);
|
|
||||||
error:
|
|
||||||
if (fid)
|
|
||||||
p9_client_clunk(fid);
|
|
||||||
v9fs_put_acl(dacl, pacl);
|
|
||||||
return err;
|
|
||||||
}
|
|
||||||
|
|
||||||
static int
|
|
||||||
v9fs_vfs_getattr_dotl(struct vfsmount *mnt, struct dentry *dentry,
|
|
||||||
struct kstat *stat)
|
|
||||||
{
|
|
||||||
struct v9fs_session_info *v9ses;
|
|
||||||
struct p9_fid *fid;
|
|
||||||
struct p9_stat_dotl *st;
|
|
||||||
|
|
||||||
p9_debug(P9_DEBUG_VFS, "dentry: %p\n", dentry);
|
|
||||||
v9ses = v9fs_dentry2v9ses(dentry);
|
|
||||||
if (v9ses->cache == CACHE_LOOSE || v9ses->cache == CACHE_FSCACHE) {
|
|
||||||
generic_fillattr(d_inode(dentry), stat);
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
fid = v9fs_fid_lookup(dentry);
|
|
||||||
if (IS_ERR(fid))
|
|
||||||
return PTR_ERR(fid);
|
|
||||||
|
|
||||||
/* Ask for all the fields in stat structure. Server will return
|
|
||||||
* whatever it supports
|
|
||||||
*/
|
|
||||||
|
|
||||||
st = p9_client_getattr_dotl(fid, P9_STATS_ALL);
|
|
||||||
if (IS_ERR(st))
|
|
||||||
return PTR_ERR(st);
|
|
||||||
|
|
||||||
v9fs_stat2inode_dotl(st, d_inode(dentry), 0);
|
|
||||||
generic_fillattr(d_inode(dentry), stat);
|
|
||||||
/* Change block size to what the server returned */
|
|
||||||
stat->blksize = st->st_blksize;
|
|
||||||
|
|
||||||
kfree(st);
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
/*
|
|
||||||
* Attribute flags.
|
|
||||||
*/
|
|
||||||
#define P9_ATTR_MODE (1 << 0)
|
|
||||||
#define P9_ATTR_UID (1 << 1)
|
|
||||||
#define P9_ATTR_GID (1 << 2)
|
|
||||||
#define P9_ATTR_SIZE (1 << 3)
|
|
||||||
#define P9_ATTR_ATIME (1 << 4)
|
|
||||||
#define P9_ATTR_MTIME (1 << 5)
|
|
||||||
#define P9_ATTR_CTIME (1 << 6)
|
|
||||||
#define P9_ATTR_ATIME_SET (1 << 7)
|
|
||||||
#define P9_ATTR_MTIME_SET (1 << 8)
|
|
||||||
|
|
||||||
struct dotl_iattr_map {
|
|
||||||
int iattr_valid;
|
|
||||||
int p9_iattr_valid;
|
|
||||||
};
|
|
||||||
|
|
||||||
static int v9fs_mapped_iattr_valid(int iattr_valid)
|
|
||||||
{
|
|
||||||
int i;
|
|
||||||
int p9_iattr_valid = 0;
|
|
||||||
struct dotl_iattr_map dotl_iattr_map[] = {
|
|
||||||
{ ATTR_MODE, P9_ATTR_MODE },
|
|
||||||
{ ATTR_UID, P9_ATTR_UID },
|
|
||||||
{ ATTR_GID, P9_ATTR_GID },
|
|
||||||
{ ATTR_SIZE, P9_ATTR_SIZE },
|
|
||||||
{ ATTR_ATIME, P9_ATTR_ATIME },
|
|
||||||
{ ATTR_MTIME, P9_ATTR_MTIME },
|
|
||||||
{ ATTR_CTIME, P9_ATTR_CTIME },
|
|
||||||
{ ATTR_ATIME_SET, P9_ATTR_ATIME_SET },
|
|
||||||
{ ATTR_MTIME_SET, P9_ATTR_MTIME_SET },
|
|
||||||
};
|
|
||||||
for (i = 0; i < ARRAY_SIZE(dotl_iattr_map); i++) {
|
|
||||||
if (iattr_valid & dotl_iattr_map[i].iattr_valid)
|
|
||||||
p9_iattr_valid |= dotl_iattr_map[i].p9_iattr_valid;
|
|
||||||
}
|
|
||||||
return p9_iattr_valid;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* v9fs_vfs_setattr_dotl - set file metadata
|
|
||||||
* @dentry: file whose metadata to set
|
|
||||||
* @iattr: metadata assignment structure
|
|
||||||
*
|
|
||||||
*/
|
|
||||||
|
|
||||||
int v9fs_vfs_setattr_dotl(struct dentry *dentry, struct iattr *iattr)
|
|
||||||
{
|
|
||||||
int retval;
|
|
||||||
struct p9_fid *fid;
|
|
||||||
struct p9_iattr_dotl p9attr;
|
|
||||||
struct inode *inode = d_inode(dentry);
|
|
||||||
|
|
||||||
p9_debug(P9_DEBUG_VFS, "\n");
|
|
||||||
|
|
||||||
retval = inode_change_ok(inode, iattr);
|
|
||||||
if (retval)
|
|
||||||
return retval;
|
|
||||||
|
|
||||||
p9attr.valid = v9fs_mapped_iattr_valid(iattr->ia_valid);
|
|
||||||
p9attr.mode = iattr->ia_mode;
|
|
||||||
p9attr.uid = iattr->ia_uid;
|
|
||||||
p9attr.gid = iattr->ia_gid;
|
|
||||||
p9attr.size = iattr->ia_size;
|
|
||||||
p9attr.atime_sec = iattr->ia_atime.tv_sec;
|
|
||||||
p9attr.atime_nsec = iattr->ia_atime.tv_nsec;
|
|
||||||
p9attr.mtime_sec = iattr->ia_mtime.tv_sec;
|
|
||||||
p9attr.mtime_nsec = iattr->ia_mtime.tv_nsec;
|
|
||||||
|
|
||||||
fid = v9fs_fid_lookup(dentry);
|
|
||||||
if (IS_ERR(fid))
|
|
||||||
return PTR_ERR(fid);
|
|
||||||
|
|
||||||
/* Write all dirty data */
|
|
||||||
if (S_ISREG(inode->i_mode))
|
|
||||||
filemap_write_and_wait(inode->i_mapping);
|
|
||||||
|
|
||||||
retval = p9_client_setattr(fid, &p9attr);
|
|
||||||
if (retval < 0)
|
|
||||||
return retval;
|
|
||||||
|
|
||||||
if ((iattr->ia_valid & ATTR_SIZE) &&
|
|
||||||
iattr->ia_size != i_size_read(inode))
|
|
||||||
truncate_setsize(inode, iattr->ia_size);
|
|
||||||
|
|
||||||
v9fs_invalidate_inode_attr(inode);
|
|
||||||
setattr_copy(inode, iattr);
|
|
||||||
mark_inode_dirty(inode);
|
|
||||||
if (iattr->ia_valid & ATTR_MODE) {
|
|
||||||
/* We also want to update ACL when we update mode bits */
|
|
||||||
retval = v9fs_acl_chmod(inode, fid);
|
|
||||||
if (retval < 0)
|
|
||||||
return retval;
|
|
||||||
}
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* v9fs_stat2inode_dotl - populate an inode structure with stat info
|
|
||||||
* @stat: stat structure
|
|
||||||
* @inode: inode to populate
|
|
||||||
* @flags: ctrl flags (e.g. V9FS_STAT2INODE_KEEP_ISIZE)
|
|
||||||
*
|
|
||||||
*/
|
|
||||||
|
|
||||||
void
|
|
||||||
v9fs_stat2inode_dotl(struct p9_stat_dotl *stat, struct inode *inode,
|
|
||||||
unsigned int flags)
|
|
||||||
{
|
|
||||||
umode_t mode;
|
|
||||||
struct v9fs_inode *v9inode = V9FS_I(inode);
|
|
||||||
|
|
||||||
if ((stat->st_result_mask & P9_STATS_BASIC) == P9_STATS_BASIC) {
|
|
||||||
inode->i_atime.tv_sec = stat->st_atime_sec;
|
|
||||||
inode->i_atime.tv_nsec = stat->st_atime_nsec;
|
|
||||||
inode->i_mtime.tv_sec = stat->st_mtime_sec;
|
|
||||||
inode->i_mtime.tv_nsec = stat->st_mtime_nsec;
|
|
||||||
inode->i_ctime.tv_sec = stat->st_ctime_sec;
|
|
||||||
inode->i_ctime.tv_nsec = stat->st_ctime_nsec;
|
|
||||||
inode->i_uid = stat->st_uid;
|
|
||||||
inode->i_gid = stat->st_gid;
|
|
||||||
set_nlink(inode, stat->st_nlink);
|
|
||||||
|
|
||||||
mode = stat->st_mode & S_IALLUGO;
|
|
||||||
mode |= inode->i_mode & ~S_IALLUGO;
|
|
||||||
inode->i_mode = mode;
|
|
||||||
|
|
||||||
if (!(flags & V9FS_STAT2INODE_KEEP_ISIZE))
|
|
||||||
v9fs_i_size_write(inode, stat->st_size);
|
|
||||||
inode->i_blocks = stat->st_blocks;
|
|
||||||
} else {
|
|
||||||
if (stat->st_result_mask & P9_STATS_ATIME) {
|
|
||||||
inode->i_atime.tv_sec = stat->st_atime_sec;
|
|
||||||
inode->i_atime.tv_nsec = stat->st_atime_nsec;
|
|
||||||
}
|
|
||||||
if (stat->st_result_mask & P9_STATS_MTIME) {
|
|
||||||
inode->i_mtime.tv_sec = stat->st_mtime_sec;
|
|
||||||
inode->i_mtime.tv_nsec = stat->st_mtime_nsec;
|
|
||||||
}
|
|
||||||
if (stat->st_result_mask & P9_STATS_CTIME) {
|
|
||||||
inode->i_ctime.tv_sec = stat->st_ctime_sec;
|
|
||||||
inode->i_ctime.tv_nsec = stat->st_ctime_nsec;
|
|
||||||
}
|
|
||||||
if (stat->st_result_mask & P9_STATS_UID)
|
|
||||||
inode->i_uid = stat->st_uid;
|
|
||||||
if (stat->st_result_mask & P9_STATS_GID)
|
|
||||||
inode->i_gid = stat->st_gid;
|
|
||||||
if (stat->st_result_mask & P9_STATS_NLINK)
|
|
||||||
set_nlink(inode, stat->st_nlink);
|
|
||||||
if (stat->st_result_mask & P9_STATS_MODE) {
|
|
||||||
inode->i_mode = stat->st_mode;
|
|
||||||
if ((S_ISBLK(inode->i_mode)) ||
|
|
||||||
(S_ISCHR(inode->i_mode)))
|
|
||||||
init_special_inode(inode, inode->i_mode,
|
|
||||||
inode->i_rdev);
|
|
||||||
}
|
|
||||||
if (stat->st_result_mask & P9_STATS_RDEV)
|
|
||||||
inode->i_rdev = new_decode_dev(stat->st_rdev);
|
|
||||||
if (!(flags & V9FS_STAT2INODE_KEEP_ISIZE) &&
|
|
||||||
stat->st_result_mask & P9_STATS_SIZE)
|
|
||||||
v9fs_i_size_write(inode, stat->st_size);
|
|
||||||
if (stat->st_result_mask & P9_STATS_BLOCKS)
|
|
||||||
inode->i_blocks = stat->st_blocks;
|
|
||||||
}
|
|
||||||
if (stat->st_result_mask & P9_STATS_GEN)
|
|
||||||
inode->i_generation = stat->st_gen;
|
|
||||||
|
|
||||||
/* Currently we don't support P9_STATS_BTIME and P9_STATS_DATA_VERSION
|
|
||||||
* because the inode structure does not have fields for them.
|
|
||||||
*/
|
|
||||||
v9inode->cache_validity &= ~V9FS_INO_INVALID_ATTR;
|
|
||||||
}
|
|
||||||
|
|
||||||
static int
|
|
||||||
v9fs_vfs_symlink_dotl(struct inode *dir, struct dentry *dentry,
|
|
||||||
const char *symname)
|
|
||||||
{
|
|
||||||
int err;
|
|
||||||
kgid_t gid;
|
|
||||||
char *name;
|
|
||||||
struct p9_qid qid;
|
|
||||||
struct inode *inode;
|
|
||||||
struct p9_fid *dfid;
|
|
||||||
struct p9_fid *fid = NULL;
|
|
||||||
struct v9fs_session_info *v9ses;
|
|
||||||
|
|
||||||
name = (char *) dentry->d_name.name;
|
|
||||||
p9_debug(P9_DEBUG_VFS, "%lu,%s,%s\n", dir->i_ino, name, symname);
|
|
||||||
v9ses = v9fs_inode2v9ses(dir);
|
|
||||||
|
|
||||||
dfid = v9fs_fid_lookup(dentry->d_parent);
|
|
||||||
if (IS_ERR(dfid)) {
|
|
||||||
err = PTR_ERR(dfid);
|
|
||||||
p9_debug(P9_DEBUG_VFS, "fid lookup failed %d\n", err);
|
|
||||||
return err;
|
|
||||||
}
|
|
||||||
|
|
||||||
gid = v9fs_get_fsgid_for_create(dir);
|
|
||||||
|
|
||||||
/* Server doesn't alter fid on TSYMLINK. Hence no need to clone it. */
|
|
||||||
err = p9_client_symlink(dfid, name, (char *)symname, gid, &qid);
|
|
||||||
|
|
||||||
if (err < 0) {
|
|
||||||
p9_debug(P9_DEBUG_VFS, "p9_client_symlink failed %d\n", err);
|
|
||||||
goto error;
|
|
||||||
}
|
|
||||||
|
|
||||||
v9fs_invalidate_inode_attr(dir);
|
|
||||||
if (v9ses->cache == CACHE_LOOSE || v9ses->cache == CACHE_FSCACHE) {
|
|
||||||
/* Now walk from the parent so we can get an unopened fid. */
|
|
||||||
fid = p9_client_walk(dfid, 1, &name, 1);
|
|
||||||
if (IS_ERR(fid)) {
|
|
||||||
err = PTR_ERR(fid);
|
|
||||||
p9_debug(P9_DEBUG_VFS, "p9_client_walk failed %d\n",
|
|
||||||
err);
|
|
||||||
fid = NULL;
|
|
||||||
goto error;
|
|
||||||
}
|
|
||||||
|
|
||||||
/* instantiate inode and assign the unopened fid to dentry */
|
|
||||||
inode = v9fs_get_new_inode_from_fid(v9ses, fid, dir->i_sb);
|
|
||||||
if (IS_ERR(inode)) {
|
|
||||||
err = PTR_ERR(inode);
|
|
||||||
p9_debug(P9_DEBUG_VFS, "inode creation failed %d\n",
|
|
||||||
err);
|
|
||||||
goto error;
|
|
||||||
}
|
|
||||||
v9fs_fid_add(dentry, fid);
|
|
||||||
d_instantiate(dentry, inode);
|
|
||||||
fid = NULL;
|
|
||||||
err = 0;
|
|
||||||
} else {
|
|
||||||
/* Not in cached mode. No need to populate inode with stat */
|
|
||||||
inode = v9fs_get_inode(dir->i_sb, S_IFLNK, 0);
|
|
||||||
if (IS_ERR(inode)) {
|
|
||||||
err = PTR_ERR(inode);
|
|
||||||
goto error;
|
|
||||||
}
|
|
||||||
d_instantiate(dentry, inode);
|
|
||||||
}
|
|
||||||
|
|
||||||
error:
|
|
||||||
if (fid)
|
|
||||||
p9_client_clunk(fid);
|
|
||||||
|
|
||||||
return err;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* v9fs_vfs_link_dotl - create a hardlink for dotl
|
|
||||||
* @old_dentry: dentry for file to link to
|
|
||||||
* @dir: inode destination for new link
|
|
||||||
* @dentry: dentry for link
|
|
||||||
*
|
|
||||||
*/
|
|
||||||
|
|
||||||
static int
|
|
||||||
v9fs_vfs_link_dotl(struct dentry *old_dentry, struct inode *dir,
|
|
||||||
struct dentry *dentry)
|
|
||||||
{
|
|
||||||
int err;
|
|
||||||
struct dentry *dir_dentry;
|
|
||||||
struct p9_fid *dfid, *oldfid;
|
|
||||||
struct v9fs_session_info *v9ses;
|
|
||||||
|
|
||||||
p9_debug(P9_DEBUG_VFS, "dir ino: %lu, old_name: %pd, new_name: %pd\n",
|
|
||||||
dir->i_ino, old_dentry, dentry);
|
|
||||||
|
|
||||||
v9ses = v9fs_inode2v9ses(dir);
|
|
||||||
dir_dentry = dentry->d_parent;
|
|
||||||
dfid = v9fs_fid_lookup(dir_dentry);
|
|
||||||
if (IS_ERR(dfid))
|
|
||||||
return PTR_ERR(dfid);
|
|
||||||
|
|
||||||
oldfid = v9fs_fid_lookup(old_dentry);
|
|
||||||
if (IS_ERR(oldfid))
|
|
||||||
return PTR_ERR(oldfid);
|
|
||||||
|
|
||||||
err = p9_client_link(dfid, oldfid, (char *)dentry->d_name.name);
|
|
||||||
|
|
||||||
if (err < 0) {
|
|
||||||
p9_debug(P9_DEBUG_VFS, "p9_client_link failed %d\n", err);
|
|
||||||
return err;
|
|
||||||
}
|
|
||||||
|
|
||||||
v9fs_invalidate_inode_attr(dir);
|
|
||||||
if (v9ses->cache == CACHE_LOOSE || v9ses->cache == CACHE_FSCACHE) {
|
|
||||||
/* Get the latest stat info from server. */
|
|
||||||
struct p9_fid *fid;
|
|
||||||
fid = v9fs_fid_lookup(old_dentry);
|
|
||||||
if (IS_ERR(fid))
|
|
||||||
return PTR_ERR(fid);
|
|
||||||
|
|
||||||
v9fs_refresh_inode_dotl(fid, d_inode(old_dentry));
|
|
||||||
}
|
|
||||||
ihold(d_inode(old_dentry));
|
|
||||||
d_instantiate(dentry, d_inode(old_dentry));
|
|
||||||
|
|
||||||
return err;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* v9fs_vfs_mknod_dotl - create a special file
|
|
||||||
* @dir: inode destination for new link
|
|
||||||
* @dentry: dentry for file
|
|
||||||
* @omode: mode for creation
|
|
||||||
* @rdev: device associated with special file
|
|
||||||
*
|
|
||||||
*/
|
|
||||||
static int
|
|
||||||
v9fs_vfs_mknod_dotl(struct inode *dir, struct dentry *dentry, umode_t omode,
|
|
||||||
dev_t rdev)
|
|
||||||
{
|
|
||||||
int err;
|
|
||||||
kgid_t gid;
|
|
||||||
char *name;
|
|
||||||
umode_t mode;
|
|
||||||
struct v9fs_session_info *v9ses;
|
|
||||||
struct p9_fid *fid = NULL, *dfid = NULL;
|
|
||||||
struct inode *inode;
|
|
||||||
struct p9_qid qid;
|
|
||||||
struct dentry *dir_dentry;
|
|
||||||
struct posix_acl *dacl = NULL, *pacl = NULL;
|
|
||||||
|
|
||||||
p9_debug(P9_DEBUG_VFS, " %lu,%pd mode: %hx MAJOR: %u MINOR: %u\n",
|
|
||||||
dir->i_ino, dentry, omode,
|
|
||||||
MAJOR(rdev), MINOR(rdev));
|
|
||||||
|
|
||||||
v9ses = v9fs_inode2v9ses(dir);
|
|
||||||
dir_dentry = dentry->d_parent;
|
|
||||||
dfid = v9fs_fid_lookup(dir_dentry);
|
|
||||||
if (IS_ERR(dfid)) {
|
|
||||||
err = PTR_ERR(dfid);
|
|
||||||
p9_debug(P9_DEBUG_VFS, "fid lookup failed %d\n", err);
|
|
||||||
dfid = NULL;
|
|
||||||
goto error;
|
|
||||||
}
|
|
||||||
|
|
||||||
gid = v9fs_get_fsgid_for_create(dir);
|
|
||||||
mode = omode;
|
|
||||||
/* Update mode based on ACL value */
|
|
||||||
err = v9fs_acl_mode(dir, &mode, &dacl, &pacl);
|
|
||||||
if (err) {
|
|
||||||
p9_debug(P9_DEBUG_VFS, "Failed to get acl values in mknod %d\n",
|
|
||||||
err);
|
|
||||||
goto error;
|
|
||||||
}
|
|
||||||
name = (char *) dentry->d_name.name;
|
|
||||||
|
|
||||||
err = p9_client_mknod_dotl(dfid, name, mode, rdev, gid, &qid);
|
|
||||||
if (err < 0)
|
|
||||||
goto error;
|
|
||||||
|
|
||||||
v9fs_invalidate_inode_attr(dir);
|
|
||||||
fid = p9_client_walk(dfid, 1, &name, 1);
|
|
||||||
if (IS_ERR(fid)) {
|
|
||||||
err = PTR_ERR(fid);
|
|
||||||
p9_debug(P9_DEBUG_VFS, "p9_client_walk failed %d\n",
|
|
||||||
err);
|
|
||||||
fid = NULL;
|
|
||||||
goto error;
|
|
||||||
}
|
|
||||||
|
|
||||||
/* instantiate inode and assign the unopened fid to the dentry */
|
|
||||||
if (v9ses->cache == CACHE_LOOSE || v9ses->cache == CACHE_FSCACHE) {
|
|
||||||
inode = v9fs_get_new_inode_from_fid(v9ses, fid, dir->i_sb);
|
|
||||||
if (IS_ERR(inode)) {
|
|
||||||
err = PTR_ERR(inode);
|
|
||||||
p9_debug(P9_DEBUG_VFS, "inode creation failed %d\n",
|
|
||||||
err);
|
|
||||||
goto error;
|
|
||||||
}
|
|
||||||
v9fs_set_create_acl(inode, fid, dacl, pacl);
|
|
||||||
v9fs_fid_add(dentry, fid);
|
|
||||||
d_instantiate(dentry, inode);
|
|
||||||
fid = NULL;
|
|
||||||
err = 0;
|
|
||||||
} else {
|
|
||||||
/*
|
|
||||||
* Not in cached mode. No need to populate inode with stat.
|
|
||||||
* socket syscall returns a fd, so we need instantiate
|
|
||||||
*/
|
|
||||||
inode = v9fs_get_inode(dir->i_sb, mode, rdev);
|
|
||||||
if (IS_ERR(inode)) {
|
|
||||||
err = PTR_ERR(inode);
|
|
||||||
goto error;
|
|
||||||
}
|
|
||||||
v9fs_set_create_acl(inode, fid, dacl, pacl);
|
|
||||||
d_instantiate(dentry, inode);
|
|
||||||
}
|
|
||||||
error:
|
|
||||||
if (fid)
|
|
||||||
p9_client_clunk(fid);
|
|
||||||
v9fs_put_acl(dacl, pacl);
|
|
||||||
return err;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* v9fs_vfs_follow_link_dotl - follow a symlink path
|
|
||||||
* @dentry: dentry for symlink
|
|
||||||
* @cookie: place to pass the data to put_link()
|
|
||||||
*/
|
|
||||||
|
|
||||||
static const char *
|
|
||||||
v9fs_vfs_follow_link_dotl(struct dentry *dentry, void **cookie)
|
|
||||||
{
|
|
||||||
struct p9_fid *fid = v9fs_fid_lookup(dentry);
|
|
||||||
char *target;
|
|
||||||
int retval;
|
|
||||||
|
|
||||||
p9_debug(P9_DEBUG_VFS, "%pd\n", dentry);
|
|
||||||
|
|
||||||
if (IS_ERR(fid))
|
|
||||||
return ERR_CAST(fid);
|
|
||||||
retval = p9_client_readlink(fid, &target);
|
|
||||||
if (retval)
|
|
||||||
return ERR_PTR(retval);
|
|
||||||
return *cookie = target;
|
|
||||||
}
|
|
||||||
|
|
||||||
int v9fs_refresh_inode_dotl(struct p9_fid *fid, struct inode *inode)
|
|
||||||
{
|
|
||||||
struct p9_stat_dotl *st;
|
|
||||||
struct v9fs_session_info *v9ses;
|
|
||||||
unsigned int flags;
|
|
||||||
|
|
||||||
v9ses = v9fs_inode2v9ses(inode);
|
|
||||||
st = p9_client_getattr_dotl(fid, P9_STATS_ALL);
|
|
||||||
if (IS_ERR(st))
|
|
||||||
return PTR_ERR(st);
|
|
||||||
/*
|
|
||||||
* Don't update inode if the file type is different
|
|
||||||
*/
|
|
||||||
if ((inode->i_mode & S_IFMT) != (st->st_mode & S_IFMT))
|
|
||||||
goto out;
|
|
||||||
|
|
||||||
/*
|
|
||||||
* We don't want to refresh inode->i_size,
|
|
||||||
* because we may have cached data
|
|
||||||
*/
|
|
||||||
flags = (v9ses->cache == CACHE_LOOSE || v9ses->cache == CACHE_FSCACHE) ?
|
|
||||||
V9FS_STAT2INODE_KEEP_ISIZE : 0;
|
|
||||||
v9fs_stat2inode_dotl(st, inode, flags);
|
|
||||||
out:
|
|
||||||
kfree(st);
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
const struct inode_operations v9fs_dir_inode_operations_dotl = {
|
|
||||||
.create = v9fs_vfs_create_dotl,
|
|
||||||
.atomic_open = v9fs_vfs_atomic_open_dotl,
|
|
||||||
.lookup = v9fs_vfs_lookup,
|
|
||||||
.link = v9fs_vfs_link_dotl,
|
|
||||||
.symlink = v9fs_vfs_symlink_dotl,
|
|
||||||
.unlink = v9fs_vfs_unlink,
|
|
||||||
.mkdir = v9fs_vfs_mkdir_dotl,
|
|
||||||
.rmdir = v9fs_vfs_rmdir,
|
|
||||||
.mknod = v9fs_vfs_mknod_dotl,
|
|
||||||
.rename = v9fs_vfs_rename,
|
|
||||||
.getattr = v9fs_vfs_getattr_dotl,
|
|
||||||
.setattr = v9fs_vfs_setattr_dotl,
|
|
||||||
.setxattr = generic_setxattr,
|
|
||||||
.getxattr = generic_getxattr,
|
|
||||||
.removexattr = generic_removexattr,
|
|
||||||
.listxattr = v9fs_listxattr,
|
|
||||||
.get_acl = v9fs_iop_get_acl,
|
|
||||||
};
|
|
||||||
|
|
||||||
const struct inode_operations v9fs_file_inode_operations_dotl = {
|
|
||||||
.getattr = v9fs_vfs_getattr_dotl,
|
|
||||||
.setattr = v9fs_vfs_setattr_dotl,
|
|
||||||
.setxattr = generic_setxattr,
|
|
||||||
.getxattr = generic_getxattr,
|
|
||||||
.removexattr = generic_removexattr,
|
|
||||||
.listxattr = v9fs_listxattr,
|
|
||||||
.get_acl = v9fs_iop_get_acl,
|
|
||||||
};
|
|
||||||
|
|
||||||
const struct inode_operations v9fs_symlink_inode_operations_dotl = {
|
|
||||||
.readlink = generic_readlink,
|
|
||||||
.follow_link = v9fs_vfs_follow_link_dotl,
|
|
||||||
.put_link = kfree_put_link,
|
|
||||||
.getattr = v9fs_vfs_getattr_dotl,
|
|
||||||
.setattr = v9fs_vfs_setattr_dotl,
|
|
||||||
.setxattr = generic_setxattr,
|
|
||||||
.getxattr = generic_getxattr,
|
|
||||||
.removexattr = generic_removexattr,
|
|
||||||
.listxattr = v9fs_listxattr,
|
|
||||||
};
|
|
@ -1,366 +0,0 @@
|
|||||||
/*
|
|
||||||
* linux/fs/9p/vfs_super.c
|
|
||||||
*
|
|
||||||
* This file contians superblock ops for 9P2000. It is intended that
|
|
||||||
* you mount this file system on directories.
|
|
||||||
*
|
|
||||||
* Copyright (C) 2004 by Eric Van Hensbergen <ericvh@gmail.com>
|
|
||||||
* Copyright (C) 2002 by Ron Minnich <rminnich@lanl.gov>
|
|
||||||
*
|
|
||||||
* This program is free software; you can redistribute it and/or modify
|
|
||||||
* it under the terms of the GNU General Public License version 2
|
|
||||||
* as published by the Free Software Foundation.
|
|
||||||
*
|
|
||||||
* This program is distributed in the hope that it will be useful,
|
|
||||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
||||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
||||||
* GNU General Public License for more details.
|
|
||||||
*
|
|
||||||
* You should have received a copy of the GNU General Public License
|
|
||||||
* along with this program; if not, write to:
|
|
||||||
* Free Software Foundation
|
|
||||||
* 51 Franklin Street, Fifth Floor
|
|
||||||
* Boston, MA 02111-1301 USA
|
|
||||||
*
|
|
||||||
*/
|
|
||||||
|
|
||||||
#include <linux/kernel.h>
|
|
||||||
#include <linux/module.h>
|
|
||||||
#include <linux/errno.h>
|
|
||||||
#include <linux/fs.h>
|
|
||||||
#include <linux/file.h>
|
|
||||||
#include <linux/stat.h>
|
|
||||||
#include <linux/string.h>
|
|
||||||
#include <linux/inet.h>
|
|
||||||
#include <linux/pagemap.h>
|
|
||||||
#include <linux/seq_file.h>
|
|
||||||
#include <linux/mount.h>
|
|
||||||
#include <linux/idr.h>
|
|
||||||
#include <linux/sched.h>
|
|
||||||
#include <linux/slab.h>
|
|
||||||
#include <linux/statfs.h>
|
|
||||||
#include <linux/magic.h>
|
|
||||||
#include <net/9p/9p.h>
|
|
||||||
#include <net/9p/client.h>
|
|
||||||
|
|
||||||
#include "v9fs.h"
|
|
||||||
#include "v9fs_vfs.h"
|
|
||||||
#include "fid.h"
|
|
||||||
#include "xattr.h"
|
|
||||||
#include "acl.h"
|
|
||||||
|
|
||||||
static const struct super_operations v9fs_super_ops, v9fs_super_ops_dotl;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* v9fs_set_super - set the superblock
|
|
||||||
* @s: super block
|
|
||||||
* @data: file system specific data
|
|
||||||
*
|
|
||||||
*/
|
|
||||||
|
|
||||||
static int v9fs_set_super(struct super_block *s, void *data)
|
|
||||||
{
|
|
||||||
s->s_fs_info = data;
|
|
||||||
return set_anon_super(s, data);
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* v9fs_fill_super - populate superblock with info
|
|
||||||
* @sb: superblock
|
|
||||||
* @v9ses: session information
|
|
||||||
* @flags: flags propagated from v9fs_mount()
|
|
||||||
*
|
|
||||||
*/
|
|
||||||
|
|
||||||
static void
|
|
||||||
v9fs_fill_super(struct super_block *sb, struct v9fs_session_info *v9ses,
|
|
||||||
int flags, void *data)
|
|
||||||
{
|
|
||||||
sb->s_maxbytes = MAX_LFS_FILESIZE;
|
|
||||||
sb->s_blocksize_bits = fls(v9ses->maxdata - 1);
|
|
||||||
sb->s_blocksize = 1 << sb->s_blocksize_bits;
|
|
||||||
sb->s_magic = V9FS_MAGIC;
|
|
||||||
if (v9fs_proto_dotl(v9ses)) {
|
|
||||||
sb->s_op = &v9fs_super_ops_dotl;
|
|
||||||
sb->s_xattr = v9fs_xattr_handlers;
|
|
||||||
} else
|
|
||||||
sb->s_op = &v9fs_super_ops;
|
|
||||||
sb->s_bdi = &v9ses->bdi;
|
|
||||||
if (v9ses->cache)
|
|
||||||
sb->s_bdi->ra_pages = (VM_MAX_READAHEAD * 1024)/PAGE_CACHE_SIZE;
|
|
||||||
|
|
||||||
sb->s_flags |= MS_ACTIVE | MS_DIRSYNC | MS_NOATIME;
|
|
||||||
if (!v9ses->cache)
|
|
||||||
sb->s_flags |= MS_SYNCHRONOUS;
|
|
||||||
|
|
||||||
#ifdef CONFIG_9P_FS_POSIX_ACL
|
|
||||||
if ((v9ses->flags & V9FS_ACL_MASK) == V9FS_POSIX_ACL)
|
|
||||||
sb->s_flags |= MS_POSIXACL;
|
|
||||||
#endif
|
|
||||||
|
|
||||||
save_mount_options(sb, data);
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* v9fs_mount - mount a superblock
|
|
||||||
* @fs_type: file system type
|
|
||||||
* @flags: mount flags
|
|
||||||
* @dev_name: device name that was mounted
|
|
||||||
* @data: mount options
|
|
||||||
*
|
|
||||||
*/
|
|
||||||
|
|
||||||
static struct dentry *v9fs_mount(struct file_system_type *fs_type, int flags,
|
|
||||||
const char *dev_name, void *data)
|
|
||||||
{
|
|
||||||
struct super_block *sb = NULL;
|
|
||||||
struct inode *inode = NULL;
|
|
||||||
struct dentry *root = NULL;
|
|
||||||
struct v9fs_session_info *v9ses = NULL;
|
|
||||||
umode_t mode = S_IRWXUGO | S_ISVTX;
|
|
||||||
struct p9_fid *fid;
|
|
||||||
int retval = 0;
|
|
||||||
|
|
||||||
p9_debug(P9_DEBUG_VFS, "\n");
|
|
||||||
|
|
||||||
v9ses = kzalloc(sizeof(struct v9fs_session_info), GFP_KERNEL);
|
|
||||||
if (!v9ses)
|
|
||||||
return ERR_PTR(-ENOMEM);
|
|
||||||
|
|
||||||
fid = v9fs_session_init(v9ses, dev_name, data);
|
|
||||||
if (IS_ERR(fid)) {
|
|
||||||
retval = PTR_ERR(fid);
|
|
||||||
goto free_session;
|
|
||||||
}
|
|
||||||
|
|
||||||
sb = sget(fs_type, NULL, v9fs_set_super, flags, v9ses);
|
|
||||||
if (IS_ERR(sb)) {
|
|
||||||
retval = PTR_ERR(sb);
|
|
||||||
goto clunk_fid;
|
|
||||||
}
|
|
||||||
v9fs_fill_super(sb, v9ses, flags, data);
|
|
||||||
|
|
||||||
if (v9ses->cache == CACHE_LOOSE || v9ses->cache == CACHE_FSCACHE)
|
|
||||||
sb->s_d_op = &v9fs_cached_dentry_operations;
|
|
||||||
else
|
|
||||||
sb->s_d_op = &v9fs_dentry_operations;
|
|
||||||
|
|
||||||
inode = v9fs_get_inode(sb, S_IFDIR | mode, 0);
|
|
||||||
if (IS_ERR(inode)) {
|
|
||||||
retval = PTR_ERR(inode);
|
|
||||||
goto release_sb;
|
|
||||||
}
|
|
||||||
|
|
||||||
root = d_make_root(inode);
|
|
||||||
if (!root) {
|
|
||||||
retval = -ENOMEM;
|
|
||||||
goto release_sb;
|
|
||||||
}
|
|
||||||
sb->s_root = root;
|
|
||||||
if (v9fs_proto_dotl(v9ses)) {
|
|
||||||
struct p9_stat_dotl *st = NULL;
|
|
||||||
st = p9_client_getattr_dotl(fid, P9_STATS_BASIC);
|
|
||||||
if (IS_ERR(st)) {
|
|
||||||
retval = PTR_ERR(st);
|
|
||||||
goto release_sb;
|
|
||||||
}
|
|
||||||
d_inode(root)->i_ino = v9fs_qid2ino(&st->qid);
|
|
||||||
v9fs_stat2inode_dotl(st, d_inode(root), 0);
|
|
||||||
kfree(st);
|
|
||||||
} else {
|
|
||||||
struct p9_wstat *st = NULL;
|
|
||||||
st = p9_client_stat(fid);
|
|
||||||
if (IS_ERR(st)) {
|
|
||||||
retval = PTR_ERR(st);
|
|
||||||
goto release_sb;
|
|
||||||
}
|
|
||||||
|
|
||||||
d_inode(root)->i_ino = v9fs_qid2ino(&st->qid);
|
|
||||||
v9fs_stat2inode(st, d_inode(root), sb, 0);
|
|
||||||
|
|
||||||
p9stat_free(st);
|
|
||||||
kfree(st);
|
|
||||||
}
|
|
||||||
retval = v9fs_get_acl(inode, fid);
|
|
||||||
if (retval)
|
|
||||||
goto release_sb;
|
|
||||||
v9fs_fid_add(root, fid);
|
|
||||||
|
|
||||||
p9_debug(P9_DEBUG_VFS, " simple set mount, return 0\n");
|
|
||||||
return dget(sb->s_root);
|
|
||||||
|
|
||||||
clunk_fid:
|
|
||||||
p9_client_clunk(fid);
|
|
||||||
v9fs_session_close(v9ses);
|
|
||||||
free_session:
|
|
||||||
kfree(v9ses);
|
|
||||||
return ERR_PTR(retval);
|
|
||||||
|
|
||||||
release_sb:
|
|
||||||
/*
|
|
||||||
* we will do the session_close and root dentry release
|
|
||||||
* in the below call. But we need to clunk fid, because we haven't
|
|
||||||
* attached the fid to dentry so it won't get clunked
|
|
||||||
* automatically.
|
|
||||||
*/
|
|
||||||
p9_client_clunk(fid);
|
|
||||||
deactivate_locked_super(sb);
|
|
||||||
return ERR_PTR(retval);
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* v9fs_kill_super - Kill Superblock
|
|
||||||
* @s: superblock
|
|
||||||
*
|
|
||||||
*/
|
|
||||||
|
|
||||||
static void v9fs_kill_super(struct super_block *s)
|
|
||||||
{
|
|
||||||
struct v9fs_session_info *v9ses = s->s_fs_info;
|
|
||||||
|
|
||||||
p9_debug(P9_DEBUG_VFS, " %p\n", s);
|
|
||||||
|
|
||||||
kill_anon_super(s);
|
|
||||||
|
|
||||||
v9fs_session_cancel(v9ses);
|
|
||||||
v9fs_session_close(v9ses);
|
|
||||||
kfree(v9ses);
|
|
||||||
s->s_fs_info = NULL;
|
|
||||||
p9_debug(P9_DEBUG_VFS, "exiting kill_super\n");
|
|
||||||
}
|
|
||||||
|
|
||||||
static void
|
|
||||||
v9fs_umount_begin(struct super_block *sb)
|
|
||||||
{
|
|
||||||
struct v9fs_session_info *v9ses;
|
|
||||||
|
|
||||||
v9ses = sb->s_fs_info;
|
|
||||||
v9fs_session_begin_cancel(v9ses);
|
|
||||||
}
|
|
||||||
|
|
||||||
static int v9fs_statfs(struct dentry *dentry, struct kstatfs *buf)
|
|
||||||
{
|
|
||||||
struct v9fs_session_info *v9ses;
|
|
||||||
struct p9_fid *fid;
|
|
||||||
struct p9_rstatfs rs;
|
|
||||||
int res;
|
|
||||||
|
|
||||||
fid = v9fs_fid_lookup(dentry);
|
|
||||||
if (IS_ERR(fid)) {
|
|
||||||
res = PTR_ERR(fid);
|
|
||||||
goto done;
|
|
||||||
}
|
|
||||||
|
|
||||||
v9ses = v9fs_dentry2v9ses(dentry);
|
|
||||||
if (v9fs_proto_dotl(v9ses)) {
|
|
||||||
res = p9_client_statfs(fid, &rs);
|
|
||||||
if (res == 0) {
|
|
||||||
buf->f_type = rs.type;
|
|
||||||
buf->f_bsize = rs.bsize;
|
|
||||||
buf->f_blocks = rs.blocks;
|
|
||||||
buf->f_bfree = rs.bfree;
|
|
||||||
buf->f_bavail = rs.bavail;
|
|
||||||
buf->f_files = rs.files;
|
|
||||||
buf->f_ffree = rs.ffree;
|
|
||||||
buf->f_fsid.val[0] = rs.fsid & 0xFFFFFFFFUL;
|
|
||||||
buf->f_fsid.val[1] = (rs.fsid >> 32) & 0xFFFFFFFFUL;
|
|
||||||
buf->f_namelen = rs.namelen;
|
|
||||||
}
|
|
||||||
if (res != -ENOSYS)
|
|
||||||
goto done;
|
|
||||||
}
|
|
||||||
res = simple_statfs(dentry, buf);
|
|
||||||
done:
|
|
||||||
return res;
|
|
||||||
}
|
|
||||||
|
|
||||||
static int v9fs_drop_inode(struct inode *inode)
|
|
||||||
{
|
|
||||||
struct v9fs_session_info *v9ses;
|
|
||||||
v9ses = v9fs_inode2v9ses(inode);
|
|
||||||
if (v9ses->cache == CACHE_LOOSE || v9ses->cache == CACHE_FSCACHE)
|
|
||||||
return generic_drop_inode(inode);
|
|
||||||
/*
|
|
||||||
* in case of non cached mode always drop the
|
|
||||||
* the inode because we want the inode attribute
|
|
||||||
* to always match that on the server.
|
|
||||||
*/
|
|
||||||
return 1;
|
|
||||||
}
|
|
||||||
|
|
||||||
static int v9fs_write_inode(struct inode *inode,
|
|
||||||
struct writeback_control *wbc)
|
|
||||||
{
|
|
||||||
int ret;
|
|
||||||
struct p9_wstat wstat;
|
|
||||||
struct v9fs_inode *v9inode;
|
|
||||||
/*
|
|
||||||
* send an fsync request to server irrespective of
|
|
||||||
* wbc->sync_mode.
|
|
||||||
*/
|
|
||||||
p9_debug(P9_DEBUG_VFS, "%s: inode %p\n", __func__, inode);
|
|
||||||
v9inode = V9FS_I(inode);
|
|
||||||
if (!v9inode->writeback_fid)
|
|
||||||
return 0;
|
|
||||||
v9fs_blank_wstat(&wstat);
|
|
||||||
|
|
||||||
ret = p9_client_wstat(v9inode->writeback_fid, &wstat);
|
|
||||||
if (ret < 0) {
|
|
||||||
__mark_inode_dirty(inode, I_DIRTY_DATASYNC);
|
|
||||||
return ret;
|
|
||||||
}
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
static int v9fs_write_inode_dotl(struct inode *inode,
|
|
||||||
struct writeback_control *wbc)
|
|
||||||
{
|
|
||||||
int ret;
|
|
||||||
struct v9fs_inode *v9inode;
|
|
||||||
/*
|
|
||||||
* send an fsync request to server irrespective of
|
|
||||||
* wbc->sync_mode.
|
|
||||||
*/
|
|
||||||
v9inode = V9FS_I(inode);
|
|
||||||
p9_debug(P9_DEBUG_VFS, "%s: inode %p, writeback_fid %p\n",
|
|
||||||
__func__, inode, v9inode->writeback_fid);
|
|
||||||
if (!v9inode->writeback_fid)
|
|
||||||
return 0;
|
|
||||||
|
|
||||||
ret = p9_client_fsync(v9inode->writeback_fid, 0);
|
|
||||||
if (ret < 0) {
|
|
||||||
__mark_inode_dirty(inode, I_DIRTY_DATASYNC);
|
|
||||||
return ret;
|
|
||||||
}
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
static const struct super_operations v9fs_super_ops = {
|
|
||||||
.alloc_inode = v9fs_alloc_inode,
|
|
||||||
.destroy_inode = v9fs_destroy_inode,
|
|
||||||
.statfs = simple_statfs,
|
|
||||||
.evict_inode = v9fs_evict_inode,
|
|
||||||
.show_options = generic_show_options,
|
|
||||||
.umount_begin = v9fs_umount_begin,
|
|
||||||
.write_inode = v9fs_write_inode,
|
|
||||||
};
|
|
||||||
|
|
||||||
static const struct super_operations v9fs_super_ops_dotl = {
|
|
||||||
.alloc_inode = v9fs_alloc_inode,
|
|
||||||
.destroy_inode = v9fs_destroy_inode,
|
|
||||||
.statfs = v9fs_statfs,
|
|
||||||
.drop_inode = v9fs_drop_inode,
|
|
||||||
.evict_inode = v9fs_evict_inode,
|
|
||||||
.show_options = generic_show_options,
|
|
||||||
.umount_begin = v9fs_umount_begin,
|
|
||||||
.write_inode = v9fs_write_inode_dotl,
|
|
||||||
};
|
|
||||||
|
|
||||||
struct file_system_type v9fs_fs_type = {
|
|
||||||
.name = "9p",
|
|
||||||
.mount = v9fs_mount,
|
|
||||||
.kill_sb = v9fs_kill_super,
|
|
||||||
.owner = THIS_MODULE,
|
|
||||||
.fs_flags = FS_RENAME_DOES_D_MOVE,
|
|
||||||
};
|
|
||||||
MODULE_ALIAS_FS("9p");
|
|
@ -1,195 +0,0 @@
|
|||||||
/*
|
|
||||||
* Copyright IBM Corporation, 2010
|
|
||||||
* Author Aneesh Kumar K.V <aneesh.kumar@linux.vnet.ibm.com>
|
|
||||||
*
|
|
||||||
* This program is free software; you can redistribute it and/or modify it
|
|
||||||
* under the terms of version 2.1 of the GNU Lesser General Public License
|
|
||||||
* as published by the Free Software Foundation.
|
|
||||||
*
|
|
||||||
* This program is distributed in the hope that it would be useful, but
|
|
||||||
* WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
||||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
|
|
||||||
*
|
|
||||||
*/
|
|
||||||
|
|
||||||
#include <linux/module.h>
|
|
||||||
#include <linux/fs.h>
|
|
||||||
#include <linux/sched.h>
|
|
||||||
#include <linux/uio.h>
|
|
||||||
#include <net/9p/9p.h>
|
|
||||||
#include <net/9p/client.h>
|
|
||||||
|
|
||||||
#include "fid.h"
|
|
||||||
#include "xattr.h"
|
|
||||||
|
|
||||||
ssize_t v9fs_fid_xattr_get(struct p9_fid *fid, const char *name,
|
|
||||||
void *buffer, size_t buffer_size)
|
|
||||||
{
|
|
||||||
ssize_t retval;
|
|
||||||
u64 attr_size;
|
|
||||||
struct p9_fid *attr_fid;
|
|
||||||
struct kvec kvec = {.iov_base = buffer, .iov_len = buffer_size};
|
|
||||||
struct iov_iter to;
|
|
||||||
int err;
|
|
||||||
|
|
||||||
iov_iter_kvec(&to, READ | ITER_KVEC, &kvec, 1, buffer_size);
|
|
||||||
|
|
||||||
attr_fid = p9_client_xattrwalk(fid, name, &attr_size);
|
|
||||||
if (IS_ERR(attr_fid)) {
|
|
||||||
retval = PTR_ERR(attr_fid);
|
|
||||||
p9_debug(P9_DEBUG_VFS, "p9_client_attrwalk failed %zd\n",
|
|
||||||
retval);
|
|
||||||
return retval;
|
|
||||||
}
|
|
||||||
if (attr_size > buffer_size) {
|
|
||||||
if (!buffer_size) /* request to get the attr_size */
|
|
||||||
retval = attr_size;
|
|
||||||
else
|
|
||||||
retval = -ERANGE;
|
|
||||||
} else {
|
|
||||||
iov_iter_truncate(&to, attr_size);
|
|
||||||
retval = p9_client_read(attr_fid, 0, &to, &err);
|
|
||||||
if (err)
|
|
||||||
retval = err;
|
|
||||||
}
|
|
||||||
p9_client_clunk(attr_fid);
|
|
||||||
return retval;
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
/*
|
|
||||||
* v9fs_xattr_get()
|
|
||||||
*
|
|
||||||
* Copy an extended attribute into the buffer
|
|
||||||
* provided, or compute the buffer size required.
|
|
||||||
* Buffer is NULL to compute the size of the buffer required.
|
|
||||||
*
|
|
||||||
* Returns a negative error number on failure, or the number of bytes
|
|
||||||
* used / required on success.
|
|
||||||
*/
|
|
||||||
ssize_t v9fs_xattr_get(struct dentry *dentry, const char *name,
|
|
||||||
void *buffer, size_t buffer_size)
|
|
||||||
{
|
|
||||||
struct p9_fid *fid;
|
|
||||||
|
|
||||||
p9_debug(P9_DEBUG_VFS, "name = %s value_len = %zu\n",
|
|
||||||
name, buffer_size);
|
|
||||||
fid = v9fs_fid_lookup(dentry);
|
|
||||||
if (IS_ERR(fid))
|
|
||||||
return PTR_ERR(fid);
|
|
||||||
|
|
||||||
return v9fs_fid_xattr_get(fid, name, buffer, buffer_size);
|
|
||||||
}
|
|
||||||
|
|
||||||
/*
|
|
||||||
* v9fs_xattr_set()
|
|
||||||
*
|
|
||||||
* Create, replace or remove an extended attribute for this inode. Buffer
|
|
||||||
* is NULL to remove an existing extended attribute, and non-NULL to
|
|
||||||
* either replace an existing extended attribute, or create a new extended
|
|
||||||
* attribute. The flags XATTR_REPLACE and XATTR_CREATE
|
|
||||||
* specify that an extended attribute must exist and must not exist
|
|
||||||
* previous to the call, respectively.
|
|
||||||
*
|
|
||||||
* Returns 0, or a negative error number on failure.
|
|
||||||
*/
|
|
||||||
int v9fs_xattr_set(struct dentry *dentry, const char *name,
|
|
||||||
const void *value, size_t value_len, int flags)
|
|
||||||
{
|
|
||||||
struct p9_fid *fid = v9fs_fid_lookup(dentry);
|
|
||||||
if (IS_ERR(fid))
|
|
||||||
return PTR_ERR(fid);
|
|
||||||
return v9fs_fid_xattr_set(fid, name, value, value_len, flags);
|
|
||||||
}
|
|
||||||
|
|
||||||
int v9fs_fid_xattr_set(struct p9_fid *fid, const char *name,
|
|
||||||
const void *value, size_t value_len, int flags)
|
|
||||||
{
|
|
||||||
struct kvec kvec = {.iov_base = (void *)value, .iov_len = value_len};
|
|
||||||
struct iov_iter from;
|
|
||||||
int retval, err;
|
|
||||||
|
|
||||||
iov_iter_kvec(&from, WRITE | ITER_KVEC, &kvec, 1, value_len);
|
|
||||||
|
|
||||||
p9_debug(P9_DEBUG_VFS, "name = %s value_len = %zu flags = %d\n",
|
|
||||||
name, value_len, flags);
|
|
||||||
|
|
||||||
/* Clone it */
|
|
||||||
fid = p9_client_walk(fid, 0, NULL, 1);
|
|
||||||
if (IS_ERR(fid))
|
|
||||||
return PTR_ERR(fid);
|
|
||||||
|
|
||||||
/*
|
|
||||||
* On success fid points to xattr
|
|
||||||
*/
|
|
||||||
retval = p9_client_xattrcreate(fid, name, value_len, flags);
|
|
||||||
if (retval < 0)
|
|
||||||
p9_debug(P9_DEBUG_VFS, "p9_client_xattrcreate failed %d\n",
|
|
||||||
retval);
|
|
||||||
else
|
|
||||||
p9_client_write(fid, 0, &from, &retval);
|
|
||||||
err = p9_client_clunk(fid);
|
|
||||||
if (!retval && err)
|
|
||||||
retval = err;
|
|
||||||
return retval;
|
|
||||||
}
|
|
||||||
|
|
||||||
ssize_t v9fs_listxattr(struct dentry *dentry, char *buffer, size_t buffer_size)
|
|
||||||
{
|
|
||||||
return v9fs_xattr_get(dentry, NULL, buffer, buffer_size);
|
|
||||||
}
|
|
||||||
|
|
||||||
static int v9fs_xattr_handler_get(const struct xattr_handler *handler,
|
|
||||||
struct dentry *dentry, const char *name,
|
|
||||||
void *buffer, size_t size)
|
|
||||||
{
|
|
||||||
const char *full_name = xattr_full_name(handler, name);
|
|
||||||
|
|
||||||
if (strcmp(name, "") == 0)
|
|
||||||
return -EINVAL;
|
|
||||||
return v9fs_xattr_get(dentry, full_name, buffer, size);
|
|
||||||
}
|
|
||||||
|
|
||||||
static int v9fs_xattr_handler_set(const struct xattr_handler *handler,
|
|
||||||
struct dentry *dentry, const char *name,
|
|
||||||
const void *value, size_t size, int flags)
|
|
||||||
{
|
|
||||||
const char *full_name = xattr_full_name(handler, name);
|
|
||||||
|
|
||||||
if (strcmp(name, "") == 0)
|
|
||||||
return -EINVAL;
|
|
||||||
return v9fs_xattr_set(dentry, full_name, value, size, flags);
|
|
||||||
}
|
|
||||||
|
|
||||||
static struct xattr_handler v9fs_xattr_user_handler = {
|
|
||||||
.prefix = XATTR_USER_PREFIX,
|
|
||||||
.get = v9fs_xattr_handler_get,
|
|
||||||
.set = v9fs_xattr_handler_set,
|
|
||||||
};
|
|
||||||
|
|
||||||
static struct xattr_handler v9fs_xattr_trusted_handler = {
|
|
||||||
.prefix = XATTR_TRUSTED_PREFIX,
|
|
||||||
.get = v9fs_xattr_handler_get,
|
|
||||||
.set = v9fs_xattr_handler_set,
|
|
||||||
};
|
|
||||||
|
|
||||||
#ifdef CONFIG_9P_FS_SECURITY
|
|
||||||
static struct xattr_handler v9fs_xattr_security_handler = {
|
|
||||||
.prefix = XATTR_SECURITY_PREFIX,
|
|
||||||
.get = v9fs_xattr_handler_get,
|
|
||||||
.set = v9fs_xattr_handler_set,
|
|
||||||
};
|
|
||||||
#endif
|
|
||||||
|
|
||||||
const struct xattr_handler *v9fs_xattr_handlers[] = {
|
|
||||||
&v9fs_xattr_user_handler,
|
|
||||||
&v9fs_xattr_trusted_handler,
|
|
||||||
#ifdef CONFIG_9P_FS_POSIX_ACL
|
|
||||||
&v9fs_xattr_acl_access_handler,
|
|
||||||
&v9fs_xattr_acl_default_handler,
|
|
||||||
#endif
|
|
||||||
#ifdef CONFIG_9P_FS_SECURITY
|
|
||||||
&v9fs_xattr_security_handler,
|
|
||||||
#endif
|
|
||||||
NULL
|
|
||||||
};
|
|
@ -1,34 +0,0 @@
|
|||||||
/*
|
|
||||||
* Copyright IBM Corporation, 2010
|
|
||||||
* Author Aneesh Kumar K.V <aneesh.kumar@linux.vnet.ibm.com>
|
|
||||||
*
|
|
||||||
* This program is free software; you can redistribute it and/or modify it
|
|
||||||
* under the terms of version 2.1 of the GNU Lesser General Public License
|
|
||||||
* as published by the Free Software Foundation.
|
|
||||||
*
|
|
||||||
* This program is distributed in the hope that it would be useful, but
|
|
||||||
* WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
||||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
|
|
||||||
*
|
|
||||||
*/
|
|
||||||
#ifndef FS_9P_XATTR_H
|
|
||||||
#define FS_9P_XATTR_H
|
|
||||||
|
|
||||||
#include <linux/xattr.h>
|
|
||||||
#include <net/9p/9p.h>
|
|
||||||
#include <net/9p/client.h>
|
|
||||||
|
|
||||||
extern const struct xattr_handler *v9fs_xattr_handlers[];
|
|
||||||
extern const struct xattr_handler v9fs_xattr_acl_access_handler;
|
|
||||||
extern const struct xattr_handler v9fs_xattr_acl_default_handler;
|
|
||||||
|
|
||||||
extern ssize_t v9fs_fid_xattr_get(struct p9_fid *, const char *,
|
|
||||||
void *, size_t);
|
|
||||||
extern ssize_t v9fs_xattr_get(struct dentry *, const char *,
|
|
||||||
void *, size_t);
|
|
||||||
extern int v9fs_fid_xattr_set(struct p9_fid *, const char *,
|
|
||||||
const void *, size_t, int);
|
|
||||||
extern int v9fs_xattr_set(struct dentry *, const char *,
|
|
||||||
const void *, size_t, int);
|
|
||||||
extern ssize_t v9fs_listxattr(struct dentry *, char *, size_t);
|
|
||||||
#endif /* FS_9P_XATTR_H */
|
|
Binary file not shown.
@ -1,12 +0,0 @@
|
|||||||
if [ "${1}" = "sys" ]; then
|
|
||||||
echo "Installing module and daemon for ACPI button"
|
|
||||||
if [ ! -f /tmpRoot/lib/modules/button.ko ]; then
|
|
||||||
cp /modules/button.ko /tmpRoot/lib/modules/
|
|
||||||
fi
|
|
||||||
tar -zxvf /addons/acpid.tgz -C /tmpRoot/
|
|
||||||
chmod 755 /tmpRoot/usr/sbin/acpid
|
|
||||||
chmod 644 /tmpRoot/etc/acpi/events/power
|
|
||||||
chmod 744 /tmpRoot/etc/acpi/power.sh
|
|
||||||
chmod 744 /tmpRoot/usr/lib/systemd/system/acpid.service
|
|
||||||
ln -sf /usr/lib/systemd/system/acpid.service /tmpRoot/etc/systemd/system/multi-user.target.wants/acpid.service
|
|
||||||
fi
|
|
@ -1,36 +0,0 @@
|
|||||||
version: 1
|
|
||||||
name: acpid
|
|
||||||
description: "Flexible and extensible daemon for delivering ACPI events"
|
|
||||||
available-for:
|
|
||||||
bromolow-3.10.108:
|
|
||||||
install-script: "install.sh"
|
|
||||||
copy: "all"
|
|
||||||
modules: true
|
|
||||||
apollolake-4.4.180:
|
|
||||||
install-script: "install.sh"
|
|
||||||
copy: "all"
|
|
||||||
modules: true
|
|
||||||
broadwell-4.4.180:
|
|
||||||
install-script: "install.sh"
|
|
||||||
copy: "all"
|
|
||||||
modules: true
|
|
||||||
broadwellnk-4.4.180:
|
|
||||||
install-script: "install.sh"
|
|
||||||
copy: "all"
|
|
||||||
modules: true
|
|
||||||
denverton-4.4.180:
|
|
||||||
install-script: "install.sh"
|
|
||||||
copy: "all"
|
|
||||||
modules: true
|
|
||||||
geminilake-4.4.180:
|
|
||||||
install-script: "install.sh"
|
|
||||||
copy: "all"
|
|
||||||
modules: true
|
|
||||||
v1000-4.4.180:
|
|
||||||
install-script: "install.sh"
|
|
||||||
copy: "all"
|
|
||||||
modules: true
|
|
||||||
purley-4.4.180:
|
|
||||||
install-script: "install.sh"
|
|
||||||
copy: "all"
|
|
||||||
modules: false
|
|
@ -1,46 +0,0 @@
|
|||||||
#
|
|
||||||
# Makefile for the Linux ACPI interpreter
|
|
||||||
#
|
|
||||||
|
|
||||||
#
|
|
||||||
# ACPI Boot-Time Table Parsing
|
|
||||||
#
|
|
||||||
#obj-$(CONFIG_X86) += blacklist.o
|
|
||||||
|
|
||||||
# Power management related files
|
|
||||||
#acpi-$(CONFIG_ACPI_SLEEP) += proc.o
|
|
||||||
|
|
||||||
|
|
||||||
#
|
|
||||||
# ACPI Bus and Device Drivers
|
|
||||||
#
|
|
||||||
#acpi-$(CONFIG_ACPI_DOCK) += dock.o
|
|
||||||
#acpi-$(CONFIG_X86_INTEL_LPSS) += acpi_lpss.o
|
|
||||||
#acpi-$(CONFIG_X86) += acpi_cmos_rtc.o
|
|
||||||
#acpi-$(CONFIG_DEBUG_FS) += debugfs.o
|
|
||||||
acpi-$(CONFIG_ACPI_NUMA) += numa.o
|
|
||||||
#acpi-$(CONFIG_ACPI_PROCFS_POWER) += cm_sbs.o
|
|
||||||
|
|
||||||
# These are (potentially) separate modules
|
|
||||||
|
|
||||||
# IPMI may be used by other drivers, so it has to initialise before them
|
|
||||||
#obj-$(CONFIG_ACPI_IPMI) += acpi_ipmi.o
|
|
||||||
|
|
||||||
#obj-$(CONFIG_ACPI_AC) += ac.o
|
|
||||||
obj-$(CONFIG_ACPI_BUTTON) += button.o
|
|
||||||
#obj-$(CONFIG_ACPI_FAN) += fan.o
|
|
||||||
#obj-$(CONFIG_ACPI_VIDEO) += video.o
|
|
||||||
#obj-$(CONFIG_ACPI_PCI_SLOT) += pci_slot.o
|
|
||||||
#obj-$(CONFIG_ACPI_CONTAINER) += container.o
|
|
||||||
#obj-$(CONFIG_ACPI_THERMAL) += thermal.o
|
|
||||||
#obj-$(CONFIG_ACPI_HOTPLUG_MEMORY) += acpi_memhotplug.o
|
|
||||||
#obj-$(CONFIG_ACPI_BATTERY) += battery.o
|
|
||||||
#obj-$(CONFIG_ACPI_SBS) += sbshc.o
|
|
||||||
#obj-$(CONFIG_ACPI_SBS) += sbs.o
|
|
||||||
#obj-$(CONFIG_ACPI_HED) += hed.o
|
|
||||||
#obj-$(CONFIG_ACPI_EC_DEBUGFS) += ec_sys.o
|
|
||||||
#obj-$(CONFIG_ACPI_CUSTOM_METHOD)+= custom_method.o
|
|
||||||
#obj-$(CONFIG_ACPI_BGRT) += bgrt.o
|
|
||||||
#obj-$(CONFIG_ACPI_I2C) += acpi_i2c.o
|
|
||||||
|
|
||||||
#obj-$(CONFIG_ACPI_PROCESSOR_AGGREGATOR) += acpi_pad.o
|
|
@ -1,454 +0,0 @@
|
|||||||
/*
|
|
||||||
* button.c - ACPI Button Driver
|
|
||||||
*
|
|
||||||
* Copyright (C) 2001, 2002 Andy Grover <andrew.grover@intel.com>
|
|
||||||
* Copyright (C) 2001, 2002 Paul Diefenbaugh <paul.s.diefenbaugh@intel.com>
|
|
||||||
*
|
|
||||||
* ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
|
||||||
*
|
|
||||||
* This program is free software; you can redistribute it and/or modify
|
|
||||||
* it under the terms of the GNU General Public License as published by
|
|
||||||
* the Free Software Foundation; either version 2 of the License, or (at
|
|
||||||
* your option) any later version.
|
|
||||||
*
|
|
||||||
* This program is distributed in the hope that it will be useful, but
|
|
||||||
* WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
||||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
|
||||||
* General Public License for more details.
|
|
||||||
*
|
|
||||||
* You should have received a copy of the GNU General Public License along
|
|
||||||
* with this program; if not, write to the Free Software Foundation, Inc.,
|
|
||||||
* 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA.
|
|
||||||
*
|
|
||||||
* ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
|
||||||
*/
|
|
||||||
|
|
||||||
#include <linux/kernel.h>
|
|
||||||
#include <linux/module.h>
|
|
||||||
#include <linux/init.h>
|
|
||||||
#include <linux/types.h>
|
|
||||||
#include <linux/proc_fs.h>
|
|
||||||
#include <linux/seq_file.h>
|
|
||||||
#include <linux/input.h>
|
|
||||||
#include <linux/slab.h>
|
|
||||||
#include <acpi/acpi_bus.h>
|
|
||||||
#include <acpi/acpi_drivers.h>
|
|
||||||
#include <acpi/button.h>
|
|
||||||
|
|
||||||
#define PREFIX "ACPI: "
|
|
||||||
|
|
||||||
#define ACPI_BUTTON_CLASS "button"
|
|
||||||
#define ACPI_BUTTON_FILE_INFO "info"
|
|
||||||
#define ACPI_BUTTON_FILE_STATE "state"
|
|
||||||
#define ACPI_BUTTON_TYPE_UNKNOWN 0x00
|
|
||||||
#define ACPI_BUTTON_NOTIFY_STATUS 0x80
|
|
||||||
|
|
||||||
#define ACPI_BUTTON_SUBCLASS_POWER "power"
|
|
||||||
#define ACPI_BUTTON_HID_POWER "PNP0C0C"
|
|
||||||
#define ACPI_BUTTON_DEVICE_NAME_POWER "Power Button"
|
|
||||||
#define ACPI_BUTTON_TYPE_POWER 0x01
|
|
||||||
|
|
||||||
#define ACPI_BUTTON_SUBCLASS_SLEEP "sleep"
|
|
||||||
#define ACPI_BUTTON_HID_SLEEP "PNP0C0E"
|
|
||||||
#define ACPI_BUTTON_DEVICE_NAME_SLEEP "Sleep Button"
|
|
||||||
#define ACPI_BUTTON_TYPE_SLEEP 0x03
|
|
||||||
|
|
||||||
#define ACPI_BUTTON_SUBCLASS_LID "lid"
|
|
||||||
#define ACPI_BUTTON_HID_LID "PNP0C0D"
|
|
||||||
#define ACPI_BUTTON_DEVICE_NAME_LID "Lid Switch"
|
|
||||||
#define ACPI_BUTTON_TYPE_LID 0x05
|
|
||||||
|
|
||||||
#define _COMPONENT ACPI_BUTTON_COMPONENT
|
|
||||||
ACPI_MODULE_NAME("button");
|
|
||||||
|
|
||||||
MODULE_AUTHOR("Paul Diefenbaugh");
|
|
||||||
MODULE_DESCRIPTION("ACPI Button Driver");
|
|
||||||
MODULE_LICENSE("GPL");
|
|
||||||
|
|
||||||
static const struct acpi_device_id button_device_ids[] = {
|
|
||||||
{ACPI_BUTTON_HID_LID, 0},
|
|
||||||
{ACPI_BUTTON_HID_SLEEP, 0},
|
|
||||||
{ACPI_BUTTON_HID_SLEEPF, 0},
|
|
||||||
{ACPI_BUTTON_HID_POWER, 0},
|
|
||||||
{ACPI_BUTTON_HID_POWERF, 0},
|
|
||||||
{"", 0},
|
|
||||||
};
|
|
||||||
MODULE_DEVICE_TABLE(acpi, button_device_ids);
|
|
||||||
|
|
||||||
static int acpi_button_add(struct acpi_device *device);
|
|
||||||
static int acpi_button_remove(struct acpi_device *device);
|
|
||||||
static void acpi_button_notify(struct acpi_device *device, u32 event);
|
|
||||||
|
|
||||||
#ifdef CONFIG_PM_SLEEP
|
|
||||||
static int acpi_button_resume(struct device *dev);
|
|
||||||
#endif
|
|
||||||
static SIMPLE_DEV_PM_OPS(acpi_button_pm, NULL, acpi_button_resume);
|
|
||||||
|
|
||||||
static struct acpi_driver acpi_button_driver = {
|
|
||||||
.name = "button",
|
|
||||||
.class = ACPI_BUTTON_CLASS,
|
|
||||||
.ids = button_device_ids,
|
|
||||||
.ops = {
|
|
||||||
.add = acpi_button_add,
|
|
||||||
.remove = acpi_button_remove,
|
|
||||||
.notify = acpi_button_notify,
|
|
||||||
},
|
|
||||||
.drv.pm = &acpi_button_pm,
|
|
||||||
};
|
|
||||||
|
|
||||||
struct acpi_button {
|
|
||||||
unsigned int type;
|
|
||||||
struct input_dev *input;
|
|
||||||
char phys[32]; /* for input device */
|
|
||||||
unsigned long pushed;
|
|
||||||
bool wakeup_enabled;
|
|
||||||
};
|
|
||||||
|
|
||||||
static BLOCKING_NOTIFIER_HEAD(acpi_lid_notifier);
|
|
||||||
static struct acpi_device *lid_device;
|
|
||||||
|
|
||||||
/* --------------------------------------------------------------------------
|
|
||||||
FS Interface (/proc)
|
|
||||||
-------------------------------------------------------------------------- */
|
|
||||||
|
|
||||||
static struct proc_dir_entry *acpi_button_dir;
|
|
||||||
static struct proc_dir_entry *acpi_lid_dir;
|
|
||||||
|
|
||||||
static int acpi_button_state_seq_show(struct seq_file *seq, void *offset)
|
|
||||||
{
|
|
||||||
struct acpi_device *device = seq->private;
|
|
||||||
acpi_status status;
|
|
||||||
unsigned long long state;
|
|
||||||
|
|
||||||
status = acpi_evaluate_integer(device->handle, "_LID", NULL, &state);
|
|
||||||
seq_printf(seq, "state: %s\n",
|
|
||||||
ACPI_FAILURE(status) ? "unsupported" :
|
|
||||||
(state ? "open" : "closed"));
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
static int acpi_button_state_open_fs(struct inode *inode, struct file *file)
|
|
||||||
{
|
|
||||||
return single_open(file, acpi_button_state_seq_show, PDE_DATA(inode));
|
|
||||||
}
|
|
||||||
|
|
||||||
static const struct file_operations acpi_button_state_fops = {
|
|
||||||
.owner = THIS_MODULE,
|
|
||||||
.open = acpi_button_state_open_fs,
|
|
||||||
.read = seq_read,
|
|
||||||
.llseek = seq_lseek,
|
|
||||||
.release = single_release,
|
|
||||||
};
|
|
||||||
|
|
||||||
static int acpi_button_add_fs(struct acpi_device *device)
|
|
||||||
{
|
|
||||||
struct acpi_button *button = acpi_driver_data(device);
|
|
||||||
struct proc_dir_entry *entry = NULL;
|
|
||||||
int ret = 0;
|
|
||||||
|
|
||||||
/* procfs I/F for ACPI lid device only */
|
|
||||||
if (button->type != ACPI_BUTTON_TYPE_LID)
|
|
||||||
return 0;
|
|
||||||
|
|
||||||
if (acpi_button_dir || acpi_lid_dir) {
|
|
||||||
printk(KERN_ERR PREFIX "More than one Lid device found!\n");
|
|
||||||
return -EEXIST;
|
|
||||||
}
|
|
||||||
|
|
||||||
/* create /proc/acpi/button */
|
|
||||||
acpi_button_dir = proc_mkdir(ACPI_BUTTON_CLASS, acpi_root_dir);
|
|
||||||
if (!acpi_button_dir)
|
|
||||||
return -ENODEV;
|
|
||||||
|
|
||||||
/* create /proc/acpi/button/lid */
|
|
||||||
acpi_lid_dir = proc_mkdir(ACPI_BUTTON_SUBCLASS_LID, acpi_button_dir);
|
|
||||||
if (!acpi_lid_dir) {
|
|
||||||
ret = -ENODEV;
|
|
||||||
goto remove_button_dir;
|
|
||||||
}
|
|
||||||
|
|
||||||
/* create /proc/acpi/button/lid/LID/ */
|
|
||||||
acpi_device_dir(device) = proc_mkdir(acpi_device_bid(device), acpi_lid_dir);
|
|
||||||
if (!acpi_device_dir(device)) {
|
|
||||||
ret = -ENODEV;
|
|
||||||
goto remove_lid_dir;
|
|
||||||
}
|
|
||||||
|
|
||||||
/* create /proc/acpi/button/lid/LID/state */
|
|
||||||
entry = proc_create_data(ACPI_BUTTON_FILE_STATE,
|
|
||||||
S_IRUGO, acpi_device_dir(device),
|
|
||||||
&acpi_button_state_fops, device);
|
|
||||||
if (!entry) {
|
|
||||||
ret = -ENODEV;
|
|
||||||
goto remove_dev_dir;
|
|
||||||
}
|
|
||||||
|
|
||||||
done:
|
|
||||||
return ret;
|
|
||||||
|
|
||||||
remove_dev_dir:
|
|
||||||
remove_proc_entry(acpi_device_bid(device),
|
|
||||||
acpi_lid_dir);
|
|
||||||
acpi_device_dir(device) = NULL;
|
|
||||||
remove_lid_dir:
|
|
||||||
remove_proc_entry(ACPI_BUTTON_SUBCLASS_LID, acpi_button_dir);
|
|
||||||
remove_button_dir:
|
|
||||||
remove_proc_entry(ACPI_BUTTON_CLASS, acpi_root_dir);
|
|
||||||
goto done;
|
|
||||||
}
|
|
||||||
|
|
||||||
static int acpi_button_remove_fs(struct acpi_device *device)
|
|
||||||
{
|
|
||||||
struct acpi_button *button = acpi_driver_data(device);
|
|
||||||
|
|
||||||
if (button->type != ACPI_BUTTON_TYPE_LID)
|
|
||||||
return 0;
|
|
||||||
|
|
||||||
remove_proc_entry(ACPI_BUTTON_FILE_STATE,
|
|
||||||
acpi_device_dir(device));
|
|
||||||
remove_proc_entry(acpi_device_bid(device),
|
|
||||||
acpi_lid_dir);
|
|
||||||
acpi_device_dir(device) = NULL;
|
|
||||||
remove_proc_entry(ACPI_BUTTON_SUBCLASS_LID, acpi_button_dir);
|
|
||||||
remove_proc_entry(ACPI_BUTTON_CLASS, acpi_root_dir);
|
|
||||||
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
/* --------------------------------------------------------------------------
|
|
||||||
Driver Interface
|
|
||||||
-------------------------------------------------------------------------- */
|
|
||||||
int acpi_lid_notifier_register(struct notifier_block *nb)
|
|
||||||
{
|
|
||||||
return blocking_notifier_chain_register(&acpi_lid_notifier, nb);
|
|
||||||
}
|
|
||||||
EXPORT_SYMBOL(acpi_lid_notifier_register);
|
|
||||||
|
|
||||||
int acpi_lid_notifier_unregister(struct notifier_block *nb)
|
|
||||||
{
|
|
||||||
return blocking_notifier_chain_unregister(&acpi_lid_notifier, nb);
|
|
||||||
}
|
|
||||||
EXPORT_SYMBOL(acpi_lid_notifier_unregister);
|
|
||||||
|
|
||||||
int acpi_lid_open(void)
|
|
||||||
{
|
|
||||||
acpi_status status;
|
|
||||||
unsigned long long state;
|
|
||||||
|
|
||||||
if (!lid_device)
|
|
||||||
return -ENODEV;
|
|
||||||
|
|
||||||
status = acpi_evaluate_integer(lid_device->handle, "_LID", NULL,
|
|
||||||
&state);
|
|
||||||
if (ACPI_FAILURE(status))
|
|
||||||
return -ENODEV;
|
|
||||||
|
|
||||||
return !!state;
|
|
||||||
}
|
|
||||||
EXPORT_SYMBOL(acpi_lid_open);
|
|
||||||
|
|
||||||
static int acpi_lid_send_state(struct acpi_device *device)
|
|
||||||
{
|
|
||||||
struct acpi_button *button = acpi_driver_data(device);
|
|
||||||
unsigned long long state;
|
|
||||||
acpi_status status;
|
|
||||||
int ret;
|
|
||||||
|
|
||||||
status = acpi_evaluate_integer(device->handle, "_LID", NULL, &state);
|
|
||||||
if (ACPI_FAILURE(status))
|
|
||||||
return -ENODEV;
|
|
||||||
|
|
||||||
/* input layer checks if event is redundant */
|
|
||||||
input_report_switch(button->input, SW_LID, !state);
|
|
||||||
input_sync(button->input);
|
|
||||||
|
|
||||||
if (state)
|
|
||||||
pm_wakeup_event(&device->dev, 0);
|
|
||||||
|
|
||||||
ret = blocking_notifier_call_chain(&acpi_lid_notifier, state, device);
|
|
||||||
if (ret == NOTIFY_DONE)
|
|
||||||
ret = blocking_notifier_call_chain(&acpi_lid_notifier, state,
|
|
||||||
device);
|
|
||||||
if (ret == NOTIFY_DONE || ret == NOTIFY_OK) {
|
|
||||||
/*
|
|
||||||
* It is also regarded as success if the notifier_chain
|
|
||||||
* returns NOTIFY_OK or NOTIFY_DONE.
|
|
||||||
*/
|
|
||||||
ret = 0;
|
|
||||||
}
|
|
||||||
return ret;
|
|
||||||
}
|
|
||||||
|
|
||||||
static void acpi_button_notify(struct acpi_device *device, u32 event)
|
|
||||||
{
|
|
||||||
struct acpi_button *button = acpi_driver_data(device);
|
|
||||||
struct input_dev *input;
|
|
||||||
|
|
||||||
switch (event) {
|
|
||||||
case ACPI_FIXED_HARDWARE_EVENT:
|
|
||||||
event = ACPI_BUTTON_NOTIFY_STATUS;
|
|
||||||
/* fall through */
|
|
||||||
case ACPI_BUTTON_NOTIFY_STATUS:
|
|
||||||
input = button->input;
|
|
||||||
if (button->type == ACPI_BUTTON_TYPE_LID) {
|
|
||||||
acpi_lid_send_state(device);
|
|
||||||
} else {
|
|
||||||
int keycode = test_bit(KEY_SLEEP, input->keybit) ?
|
|
||||||
KEY_SLEEP : KEY_POWER;
|
|
||||||
|
|
||||||
input_report_key(input, keycode, 1);
|
|
||||||
input_sync(input);
|
|
||||||
input_report_key(input, keycode, 0);
|
|
||||||
input_sync(input);
|
|
||||||
|
|
||||||
pm_wakeup_event(&device->dev, 0);
|
|
||||||
}
|
|
||||||
|
|
||||||
acpi_bus_generate_proc_event(device, event, ++button->pushed);
|
|
||||||
break;
|
|
||||||
default:
|
|
||||||
ACPI_DEBUG_PRINT((ACPI_DB_INFO,
|
|
||||||
"Unsupported event [0x%x]\n", event));
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
#ifdef CONFIG_PM_SLEEP
|
|
||||||
static int acpi_button_resume(struct device *dev)
|
|
||||||
{
|
|
||||||
struct acpi_device *device = to_acpi_device(dev);
|
|
||||||
struct acpi_button *button = acpi_driver_data(device);
|
|
||||||
|
|
||||||
if (button->type == ACPI_BUTTON_TYPE_LID)
|
|
||||||
return acpi_lid_send_state(device);
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
#endif
|
|
||||||
|
|
||||||
static int acpi_button_add(struct acpi_device *device)
|
|
||||||
{
|
|
||||||
struct acpi_button *button;
|
|
||||||
struct input_dev *input;
|
|
||||||
const char *hid = acpi_device_hid(device);
|
|
||||||
char *name, *class;
|
|
||||||
int error;
|
|
||||||
|
|
||||||
button = kzalloc(sizeof(struct acpi_button), GFP_KERNEL);
|
|
||||||
if (!button)
|
|
||||||
return -ENOMEM;
|
|
||||||
|
|
||||||
device->driver_data = button;
|
|
||||||
|
|
||||||
button->input = input = input_allocate_device();
|
|
||||||
if (!input) {
|
|
||||||
error = -ENOMEM;
|
|
||||||
goto err_free_button;
|
|
||||||
}
|
|
||||||
|
|
||||||
name = acpi_device_name(device);
|
|
||||||
class = acpi_device_class(device);
|
|
||||||
|
|
||||||
if (!strcmp(hid, ACPI_BUTTON_HID_POWER) ||
|
|
||||||
!strcmp(hid, ACPI_BUTTON_HID_POWERF)) {
|
|
||||||
button->type = ACPI_BUTTON_TYPE_POWER;
|
|
||||||
strcpy(name, ACPI_BUTTON_DEVICE_NAME_POWER);
|
|
||||||
sprintf(class, "%s/%s",
|
|
||||||
ACPI_BUTTON_CLASS, ACPI_BUTTON_SUBCLASS_POWER);
|
|
||||||
} else if (!strcmp(hid, ACPI_BUTTON_HID_SLEEP) ||
|
|
||||||
!strcmp(hid, ACPI_BUTTON_HID_SLEEPF)) {
|
|
||||||
button->type = ACPI_BUTTON_TYPE_SLEEP;
|
|
||||||
strcpy(name, ACPI_BUTTON_DEVICE_NAME_SLEEP);
|
|
||||||
sprintf(class, "%s/%s",
|
|
||||||
ACPI_BUTTON_CLASS, ACPI_BUTTON_SUBCLASS_SLEEP);
|
|
||||||
} else if (!strcmp(hid, ACPI_BUTTON_HID_LID)) {
|
|
||||||
button->type = ACPI_BUTTON_TYPE_LID;
|
|
||||||
strcpy(name, ACPI_BUTTON_DEVICE_NAME_LID);
|
|
||||||
sprintf(class, "%s/%s",
|
|
||||||
ACPI_BUTTON_CLASS, ACPI_BUTTON_SUBCLASS_LID);
|
|
||||||
} else {
|
|
||||||
printk(KERN_ERR PREFIX "Unsupported hid [%s]\n", hid);
|
|
||||||
error = -ENODEV;
|
|
||||||
goto err_free_input;
|
|
||||||
}
|
|
||||||
|
|
||||||
error = acpi_button_add_fs(device);
|
|
||||||
if (error)
|
|
||||||
goto err_free_input;
|
|
||||||
|
|
||||||
snprintf(button->phys, sizeof(button->phys), "%s/button/input0", hid);
|
|
||||||
|
|
||||||
input->name = name;
|
|
||||||
input->phys = button->phys;
|
|
||||||
input->id.bustype = BUS_HOST;
|
|
||||||
input->id.product = button->type;
|
|
||||||
input->dev.parent = &device->dev;
|
|
||||||
|
|
||||||
switch (button->type) {
|
|
||||||
case ACPI_BUTTON_TYPE_POWER:
|
|
||||||
input->evbit[0] = BIT_MASK(EV_KEY);
|
|
||||||
set_bit(KEY_POWER, input->keybit);
|
|
||||||
break;
|
|
||||||
|
|
||||||
case ACPI_BUTTON_TYPE_SLEEP:
|
|
||||||
input->evbit[0] = BIT_MASK(EV_KEY);
|
|
||||||
set_bit(KEY_SLEEP, input->keybit);
|
|
||||||
break;
|
|
||||||
|
|
||||||
case ACPI_BUTTON_TYPE_LID:
|
|
||||||
input->evbit[0] = BIT_MASK(EV_SW);
|
|
||||||
set_bit(SW_LID, input->swbit);
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
|
|
||||||
error = input_register_device(input);
|
|
||||||
if (error)
|
|
||||||
goto err_remove_fs;
|
|
||||||
if (button->type == ACPI_BUTTON_TYPE_LID) {
|
|
||||||
acpi_lid_send_state(device);
|
|
||||||
/*
|
|
||||||
* This assumes there's only one lid device, or if there are
|
|
||||||
* more we only care about the last one...
|
|
||||||
*/
|
|
||||||
lid_device = device;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (device->wakeup.flags.valid) {
|
|
||||||
/* Button's GPE is run-wake GPE */
|
|
||||||
acpi_enable_gpe(device->wakeup.gpe_device,
|
|
||||||
device->wakeup.gpe_number);
|
|
||||||
if (!device_may_wakeup(&device->dev)) {
|
|
||||||
device_set_wakeup_enable(&device->dev, true);
|
|
||||||
button->wakeup_enabled = true;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
printk(KERN_INFO PREFIX "%s [%s]\n", name, acpi_device_bid(device));
|
|
||||||
return 0;
|
|
||||||
|
|
||||||
err_remove_fs:
|
|
||||||
acpi_button_remove_fs(device);
|
|
||||||
err_free_input:
|
|
||||||
input_free_device(input);
|
|
||||||
err_free_button:
|
|
||||||
kfree(button);
|
|
||||||
return error;
|
|
||||||
}
|
|
||||||
|
|
||||||
static int acpi_button_remove(struct acpi_device *device)
|
|
||||||
{
|
|
||||||
struct acpi_button *button = acpi_driver_data(device);
|
|
||||||
|
|
||||||
if (device->wakeup.flags.valid) {
|
|
||||||
acpi_disable_gpe(device->wakeup.gpe_device,
|
|
||||||
device->wakeup.gpe_number);
|
|
||||||
if (button->wakeup_enabled)
|
|
||||||
device_set_wakeup_enable(&device->dev, false);
|
|
||||||
}
|
|
||||||
|
|
||||||
acpi_button_remove_fs(device);
|
|
||||||
input_unregister_device(button->input);
|
|
||||||
kfree(button);
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
module_acpi_driver(acpi_button_driver);
|
|
@ -1 +0,0 @@
|
|||||||
obj-$(CONFIG_ACPI_BUTTON) += button.o
|
|
@ -1,449 +0,0 @@
|
|||||||
/*
|
|
||||||
* button.c - ACPI Button Driver
|
|
||||||
*
|
|
||||||
* Copyright (C) 2001, 2002 Andy Grover <andrew.grover@intel.com>
|
|
||||||
* Copyright (C) 2001, 2002 Paul Diefenbaugh <paul.s.diefenbaugh@intel.com>
|
|
||||||
*
|
|
||||||
* ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
|
||||||
*
|
|
||||||
* This program is free software; you can redistribute it and/or modify
|
|
||||||
* it under the terms of the GNU General Public License as published by
|
|
||||||
* the Free Software Foundation; either version 2 of the License, or (at
|
|
||||||
* your option) any later version.
|
|
||||||
*
|
|
||||||
* This program is distributed in the hope that it will be useful, but
|
|
||||||
* WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
||||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
|
||||||
* General Public License for more details.
|
|
||||||
*
|
|
||||||
* ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
|
||||||
*/
|
|
||||||
|
|
||||||
#include <linux/kernel.h>
|
|
||||||
#include <linux/module.h>
|
|
||||||
#include <linux/init.h>
|
|
||||||
#include <linux/types.h>
|
|
||||||
#include <linux/proc_fs.h>
|
|
||||||
#include <linux/seq_file.h>
|
|
||||||
#include <linux/input.h>
|
|
||||||
#include <linux/slab.h>
|
|
||||||
#include <linux/acpi.h>
|
|
||||||
#include <acpi/button.h>
|
|
||||||
|
|
||||||
#define PREFIX "ACPI: "
|
|
||||||
|
|
||||||
#define ACPI_BUTTON_CLASS "button"
|
|
||||||
#define ACPI_BUTTON_FILE_INFO "info"
|
|
||||||
#define ACPI_BUTTON_FILE_STATE "state"
|
|
||||||
#define ACPI_BUTTON_TYPE_UNKNOWN 0x00
|
|
||||||
#define ACPI_BUTTON_NOTIFY_STATUS 0x80
|
|
||||||
|
|
||||||
#define ACPI_BUTTON_SUBCLASS_POWER "power"
|
|
||||||
#define ACPI_BUTTON_HID_POWER "PNP0C0C"
|
|
||||||
#define ACPI_BUTTON_DEVICE_NAME_POWER "Power Button"
|
|
||||||
#define ACPI_BUTTON_TYPE_POWER 0x01
|
|
||||||
|
|
||||||
#define ACPI_BUTTON_SUBCLASS_SLEEP "sleep"
|
|
||||||
#define ACPI_BUTTON_HID_SLEEP "PNP0C0E"
|
|
||||||
#define ACPI_BUTTON_DEVICE_NAME_SLEEP "Sleep Button"
|
|
||||||
#define ACPI_BUTTON_TYPE_SLEEP 0x03
|
|
||||||
|
|
||||||
#define ACPI_BUTTON_SUBCLASS_LID "lid"
|
|
||||||
#define ACPI_BUTTON_HID_LID "PNP0C0D"
|
|
||||||
#define ACPI_BUTTON_DEVICE_NAME_LID "Lid Switch"
|
|
||||||
#define ACPI_BUTTON_TYPE_LID 0x05
|
|
||||||
|
|
||||||
#define _COMPONENT ACPI_BUTTON_COMPONENT
|
|
||||||
ACPI_MODULE_NAME("button");
|
|
||||||
|
|
||||||
MODULE_AUTHOR("Paul Diefenbaugh");
|
|
||||||
MODULE_DESCRIPTION("ACPI Button Driver");
|
|
||||||
MODULE_LICENSE("GPL");
|
|
||||||
|
|
||||||
static const struct acpi_device_id button_device_ids[] = {
|
|
||||||
{ACPI_BUTTON_HID_LID, 0},
|
|
||||||
{ACPI_BUTTON_HID_SLEEP, 0},
|
|
||||||
{ACPI_BUTTON_HID_SLEEPF, 0},
|
|
||||||
{ACPI_BUTTON_HID_POWER, 0},
|
|
||||||
{ACPI_BUTTON_HID_POWERF, 0},
|
|
||||||
{"", 0},
|
|
||||||
};
|
|
||||||
MODULE_DEVICE_TABLE(acpi, button_device_ids);
|
|
||||||
|
|
||||||
static int acpi_button_add(struct acpi_device *device);
|
|
||||||
static int acpi_button_remove(struct acpi_device *device);
|
|
||||||
static void acpi_button_notify(struct acpi_device *device, u32 event);
|
|
||||||
|
|
||||||
#ifdef CONFIG_PM_SLEEP
|
|
||||||
static int acpi_button_suspend(struct device *dev);
|
|
||||||
static int acpi_button_resume(struct device *dev);
|
|
||||||
#else
|
|
||||||
#define acpi_button_suspend NULL
|
|
||||||
#define acpi_button_resume NULL
|
|
||||||
#endif
|
|
||||||
static SIMPLE_DEV_PM_OPS(acpi_button_pm, acpi_button_suspend, acpi_button_resume);
|
|
||||||
|
|
||||||
static struct acpi_driver acpi_button_driver = {
|
|
||||||
.name = "button",
|
|
||||||
.class = ACPI_BUTTON_CLASS,
|
|
||||||
.ids = button_device_ids,
|
|
||||||
.ops = {
|
|
||||||
.add = acpi_button_add,
|
|
||||||
.remove = acpi_button_remove,
|
|
||||||
.notify = acpi_button_notify,
|
|
||||||
},
|
|
||||||
.drv.pm = &acpi_button_pm,
|
|
||||||
};
|
|
||||||
|
|
||||||
struct acpi_button {
|
|
||||||
unsigned int type;
|
|
||||||
struct input_dev *input;
|
|
||||||
char phys[32]; /* for input device */
|
|
||||||
unsigned long pushed;
|
|
||||||
bool suspended;
|
|
||||||
};
|
|
||||||
|
|
||||||
static BLOCKING_NOTIFIER_HEAD(acpi_lid_notifier);
|
|
||||||
static struct acpi_device *lid_device;
|
|
||||||
|
|
||||||
/* --------------------------------------------------------------------------
|
|
||||||
FS Interface (/proc)
|
|
||||||
-------------------------------------------------------------------------- */
|
|
||||||
|
|
||||||
static struct proc_dir_entry *acpi_button_dir;
|
|
||||||
static struct proc_dir_entry *acpi_lid_dir;
|
|
||||||
|
|
||||||
static int acpi_button_state_seq_show(struct seq_file *seq, void *offset)
|
|
||||||
{
|
|
||||||
struct acpi_device *device = seq->private;
|
|
||||||
acpi_status status;
|
|
||||||
unsigned long long state;
|
|
||||||
|
|
||||||
status = acpi_evaluate_integer(device->handle, "_LID", NULL, &state);
|
|
||||||
seq_printf(seq, "state: %s\n",
|
|
||||||
ACPI_FAILURE(status) ? "unsupported" :
|
|
||||||
(state ? "open" : "closed"));
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
static int acpi_button_state_open_fs(struct inode *inode, struct file *file)
|
|
||||||
{
|
|
||||||
return single_open(file, acpi_button_state_seq_show, PDE_DATA(inode));
|
|
||||||
}
|
|
||||||
|
|
||||||
static const struct file_operations acpi_button_state_fops = {
|
|
||||||
.owner = THIS_MODULE,
|
|
||||||
.open = acpi_button_state_open_fs,
|
|
||||||
.read = seq_read,
|
|
||||||
.llseek = seq_lseek,
|
|
||||||
.release = single_release,
|
|
||||||
};
|
|
||||||
|
|
||||||
static int acpi_button_add_fs(struct acpi_device *device)
|
|
||||||
{
|
|
||||||
struct acpi_button *button = acpi_driver_data(device);
|
|
||||||
struct proc_dir_entry *entry = NULL;
|
|
||||||
int ret = 0;
|
|
||||||
|
|
||||||
/* procfs I/F for ACPI lid device only */
|
|
||||||
if (button->type != ACPI_BUTTON_TYPE_LID)
|
|
||||||
return 0;
|
|
||||||
|
|
||||||
if (acpi_button_dir || acpi_lid_dir) {
|
|
||||||
printk(KERN_ERR PREFIX "More than one Lid device found!\n");
|
|
||||||
return -EEXIST;
|
|
||||||
}
|
|
||||||
|
|
||||||
/* create /proc/acpi/button */
|
|
||||||
acpi_button_dir = proc_mkdir(ACPI_BUTTON_CLASS, acpi_root_dir);
|
|
||||||
if (!acpi_button_dir)
|
|
||||||
return -ENODEV;
|
|
||||||
|
|
||||||
/* create /proc/acpi/button/lid */
|
|
||||||
acpi_lid_dir = proc_mkdir(ACPI_BUTTON_SUBCLASS_LID, acpi_button_dir);
|
|
||||||
if (!acpi_lid_dir) {
|
|
||||||
ret = -ENODEV;
|
|
||||||
goto remove_button_dir;
|
|
||||||
}
|
|
||||||
|
|
||||||
/* create /proc/acpi/button/lid/LID/ */
|
|
||||||
acpi_device_dir(device) = proc_mkdir(acpi_device_bid(device), acpi_lid_dir);
|
|
||||||
if (!acpi_device_dir(device)) {
|
|
||||||
ret = -ENODEV;
|
|
||||||
goto remove_lid_dir;
|
|
||||||
}
|
|
||||||
|
|
||||||
/* create /proc/acpi/button/lid/LID/state */
|
|
||||||
entry = proc_create_data(ACPI_BUTTON_FILE_STATE,
|
|
||||||
S_IRUGO, acpi_device_dir(device),
|
|
||||||
&acpi_button_state_fops, device);
|
|
||||||
if (!entry) {
|
|
||||||
ret = -ENODEV;
|
|
||||||
goto remove_dev_dir;
|
|
||||||
}
|
|
||||||
|
|
||||||
done:
|
|
||||||
return ret;
|
|
||||||
|
|
||||||
remove_dev_dir:
|
|
||||||
remove_proc_entry(acpi_device_bid(device),
|
|
||||||
acpi_lid_dir);
|
|
||||||
acpi_device_dir(device) = NULL;
|
|
||||||
remove_lid_dir:
|
|
||||||
remove_proc_entry(ACPI_BUTTON_SUBCLASS_LID, acpi_button_dir);
|
|
||||||
remove_button_dir:
|
|
||||||
remove_proc_entry(ACPI_BUTTON_CLASS, acpi_root_dir);
|
|
||||||
goto done;
|
|
||||||
}
|
|
||||||
|
|
||||||
static int acpi_button_remove_fs(struct acpi_device *device)
|
|
||||||
{
|
|
||||||
struct acpi_button *button = acpi_driver_data(device);
|
|
||||||
|
|
||||||
if (button->type != ACPI_BUTTON_TYPE_LID)
|
|
||||||
return 0;
|
|
||||||
|
|
||||||
remove_proc_entry(ACPI_BUTTON_FILE_STATE,
|
|
||||||
acpi_device_dir(device));
|
|
||||||
remove_proc_entry(acpi_device_bid(device),
|
|
||||||
acpi_lid_dir);
|
|
||||||
acpi_device_dir(device) = NULL;
|
|
||||||
remove_proc_entry(ACPI_BUTTON_SUBCLASS_LID, acpi_button_dir);
|
|
||||||
remove_proc_entry(ACPI_BUTTON_CLASS, acpi_root_dir);
|
|
||||||
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
/* --------------------------------------------------------------------------
|
|
||||||
Driver Interface
|
|
||||||
-------------------------------------------------------------------------- */
|
|
||||||
int acpi_lid_notifier_register(struct notifier_block *nb)
|
|
||||||
{
|
|
||||||
return blocking_notifier_chain_register(&acpi_lid_notifier, nb);
|
|
||||||
}
|
|
||||||
EXPORT_SYMBOL(acpi_lid_notifier_register);
|
|
||||||
|
|
||||||
int acpi_lid_notifier_unregister(struct notifier_block *nb)
|
|
||||||
{
|
|
||||||
return blocking_notifier_chain_unregister(&acpi_lid_notifier, nb);
|
|
||||||
}
|
|
||||||
EXPORT_SYMBOL(acpi_lid_notifier_unregister);
|
|
||||||
|
|
||||||
int acpi_lid_open(void)
|
|
||||||
{
|
|
||||||
acpi_status status;
|
|
||||||
unsigned long long state;
|
|
||||||
|
|
||||||
if (!lid_device)
|
|
||||||
return -ENODEV;
|
|
||||||
|
|
||||||
status = acpi_evaluate_integer(lid_device->handle, "_LID", NULL,
|
|
||||||
&state);
|
|
||||||
if (ACPI_FAILURE(status))
|
|
||||||
return -ENODEV;
|
|
||||||
|
|
||||||
return !!state;
|
|
||||||
}
|
|
||||||
EXPORT_SYMBOL(acpi_lid_open);
|
|
||||||
|
|
||||||
static int acpi_lid_send_state(struct acpi_device *device)
|
|
||||||
{
|
|
||||||
struct acpi_button *button = acpi_driver_data(device);
|
|
||||||
unsigned long long state;
|
|
||||||
acpi_status status;
|
|
||||||
int ret;
|
|
||||||
|
|
||||||
status = acpi_evaluate_integer(device->handle, "_LID", NULL, &state);
|
|
||||||
if (ACPI_FAILURE(status))
|
|
||||||
return -ENODEV;
|
|
||||||
|
|
||||||
/* input layer checks if event is redundant */
|
|
||||||
input_report_switch(button->input, SW_LID, !state);
|
|
||||||
input_sync(button->input);
|
|
||||||
|
|
||||||
if (state)
|
|
||||||
pm_wakeup_event(&device->dev, 0);
|
|
||||||
|
|
||||||
ret = blocking_notifier_call_chain(&acpi_lid_notifier, state, device);
|
|
||||||
if (ret == NOTIFY_DONE)
|
|
||||||
ret = blocking_notifier_call_chain(&acpi_lid_notifier, state,
|
|
||||||
device);
|
|
||||||
if (ret == NOTIFY_DONE || ret == NOTIFY_OK) {
|
|
||||||
/*
|
|
||||||
* It is also regarded as success if the notifier_chain
|
|
||||||
* returns NOTIFY_OK or NOTIFY_DONE.
|
|
||||||
*/
|
|
||||||
ret = 0;
|
|
||||||
}
|
|
||||||
return ret;
|
|
||||||
}
|
|
||||||
|
|
||||||
static void acpi_button_notify(struct acpi_device *device, u32 event)
|
|
||||||
{
|
|
||||||
struct acpi_button *button = acpi_driver_data(device);
|
|
||||||
struct input_dev *input;
|
|
||||||
|
|
||||||
switch (event) {
|
|
||||||
case ACPI_FIXED_HARDWARE_EVENT:
|
|
||||||
event = ACPI_BUTTON_NOTIFY_STATUS;
|
|
||||||
/* fall through */
|
|
||||||
case ACPI_BUTTON_NOTIFY_STATUS:
|
|
||||||
input = button->input;
|
|
||||||
if (button->type == ACPI_BUTTON_TYPE_LID) {
|
|
||||||
acpi_lid_send_state(device);
|
|
||||||
} else {
|
|
||||||
int keycode;
|
|
||||||
|
|
||||||
pm_wakeup_event(&device->dev, 0);
|
|
||||||
if (button->suspended)
|
|
||||||
break;
|
|
||||||
|
|
||||||
keycode = test_bit(KEY_SLEEP, input->keybit) ?
|
|
||||||
KEY_SLEEP : KEY_POWER;
|
|
||||||
input_report_key(input, keycode, 1);
|
|
||||||
input_sync(input);
|
|
||||||
input_report_key(input, keycode, 0);
|
|
||||||
input_sync(input);
|
|
||||||
|
|
||||||
acpi_bus_generate_netlink_event(
|
|
||||||
device->pnp.device_class,
|
|
||||||
dev_name(&device->dev),
|
|
||||||
event, ++button->pushed);
|
|
||||||
}
|
|
||||||
break;
|
|
||||||
default:
|
|
||||||
ACPI_DEBUG_PRINT((ACPI_DB_INFO,
|
|
||||||
"Unsupported event [0x%x]\n", event));
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
#ifdef CONFIG_PM_SLEEP
|
|
||||||
static int acpi_button_suspend(struct device *dev)
|
|
||||||
{
|
|
||||||
struct acpi_device *device = to_acpi_device(dev);
|
|
||||||
struct acpi_button *button = acpi_driver_data(device);
|
|
||||||
|
|
||||||
button->suspended = true;
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
static int acpi_button_resume(struct device *dev)
|
|
||||||
{
|
|
||||||
struct acpi_device *device = to_acpi_device(dev);
|
|
||||||
struct acpi_button *button = acpi_driver_data(device);
|
|
||||||
|
|
||||||
button->suspended = false;
|
|
||||||
if (button->type == ACPI_BUTTON_TYPE_LID)
|
|
||||||
return acpi_lid_send_state(device);
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
#endif
|
|
||||||
|
|
||||||
static int acpi_button_add(struct acpi_device *device)
|
|
||||||
{
|
|
||||||
struct acpi_button *button;
|
|
||||||
struct input_dev *input;
|
|
||||||
const char *hid = acpi_device_hid(device);
|
|
||||||
char *name, *class;
|
|
||||||
int error;
|
|
||||||
|
|
||||||
button = kzalloc(sizeof(struct acpi_button), GFP_KERNEL);
|
|
||||||
if (!button)
|
|
||||||
return -ENOMEM;
|
|
||||||
|
|
||||||
device->driver_data = button;
|
|
||||||
|
|
||||||
button->input = input = input_allocate_device();
|
|
||||||
if (!input) {
|
|
||||||
error = -ENOMEM;
|
|
||||||
goto err_free_button;
|
|
||||||
}
|
|
||||||
|
|
||||||
name = acpi_device_name(device);
|
|
||||||
class = acpi_device_class(device);
|
|
||||||
|
|
||||||
if (!strcmp(hid, ACPI_BUTTON_HID_POWER) ||
|
|
||||||
!strcmp(hid, ACPI_BUTTON_HID_POWERF)) {
|
|
||||||
button->type = ACPI_BUTTON_TYPE_POWER;
|
|
||||||
strcpy(name, ACPI_BUTTON_DEVICE_NAME_POWER);
|
|
||||||
sprintf(class, "%s/%s",
|
|
||||||
ACPI_BUTTON_CLASS, ACPI_BUTTON_SUBCLASS_POWER);
|
|
||||||
} else if (!strcmp(hid, ACPI_BUTTON_HID_SLEEP) ||
|
|
||||||
!strcmp(hid, ACPI_BUTTON_HID_SLEEPF)) {
|
|
||||||
button->type = ACPI_BUTTON_TYPE_SLEEP;
|
|
||||||
strcpy(name, ACPI_BUTTON_DEVICE_NAME_SLEEP);
|
|
||||||
sprintf(class, "%s/%s",
|
|
||||||
ACPI_BUTTON_CLASS, ACPI_BUTTON_SUBCLASS_SLEEP);
|
|
||||||
} else if (!strcmp(hid, ACPI_BUTTON_HID_LID)) {
|
|
||||||
button->type = ACPI_BUTTON_TYPE_LID;
|
|
||||||
strcpy(name, ACPI_BUTTON_DEVICE_NAME_LID);
|
|
||||||
sprintf(class, "%s/%s",
|
|
||||||
ACPI_BUTTON_CLASS, ACPI_BUTTON_SUBCLASS_LID);
|
|
||||||
} else {
|
|
||||||
printk(KERN_ERR PREFIX "Unsupported hid [%s]\n", hid);
|
|
||||||
error = -ENODEV;
|
|
||||||
goto err_free_input;
|
|
||||||
}
|
|
||||||
|
|
||||||
error = acpi_button_add_fs(device);
|
|
||||||
if (error)
|
|
||||||
goto err_free_input;
|
|
||||||
|
|
||||||
snprintf(button->phys, sizeof(button->phys), "%s/button/input0", hid);
|
|
||||||
|
|
||||||
input->name = name;
|
|
||||||
input->phys = button->phys;
|
|
||||||
input->id.bustype = BUS_HOST;
|
|
||||||
input->id.product = button->type;
|
|
||||||
input->dev.parent = &device->dev;
|
|
||||||
|
|
||||||
switch (button->type) {
|
|
||||||
case ACPI_BUTTON_TYPE_POWER:
|
|
||||||
input_set_capability(input, EV_KEY, KEY_POWER);
|
|
||||||
break;
|
|
||||||
|
|
||||||
case ACPI_BUTTON_TYPE_SLEEP:
|
|
||||||
input_set_capability(input, EV_KEY, KEY_SLEEP);
|
|
||||||
break;
|
|
||||||
|
|
||||||
case ACPI_BUTTON_TYPE_LID:
|
|
||||||
input_set_capability(input, EV_SW, SW_LID);
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
|
|
||||||
error = input_register_device(input);
|
|
||||||
if (error)
|
|
||||||
goto err_remove_fs;
|
|
||||||
if (button->type == ACPI_BUTTON_TYPE_LID) {
|
|
||||||
acpi_lid_send_state(device);
|
|
||||||
/*
|
|
||||||
* This assumes there's only one lid device, or if there are
|
|
||||||
* more we only care about the last one...
|
|
||||||
*/
|
|
||||||
lid_device = device;
|
|
||||||
}
|
|
||||||
|
|
||||||
printk(KERN_INFO PREFIX "%s [%s]\n", name, acpi_device_bid(device));
|
|
||||||
return 0;
|
|
||||||
|
|
||||||
err_remove_fs:
|
|
||||||
acpi_button_remove_fs(device);
|
|
||||||
err_free_input:
|
|
||||||
input_free_device(input);
|
|
||||||
err_free_button:
|
|
||||||
kfree(button);
|
|
||||||
return error;
|
|
||||||
}
|
|
||||||
|
|
||||||
static int acpi_button_remove(struct acpi_device *device)
|
|
||||||
{
|
|
||||||
struct acpi_button *button = acpi_driver_data(device);
|
|
||||||
|
|
||||||
acpi_button_remove_fs(device);
|
|
||||||
input_unregister_device(button->input);
|
|
||||||
kfree(button);
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
module_acpi_driver(acpi_button_driver);
|
|
@ -1,4 +0,0 @@
|
|||||||
if [ "${1}" = "rd" ]; then
|
|
||||||
echo "Installing module for Atheros L1C Gigabit Ethernet adapter"
|
|
||||||
${INSMOD} "/modules/atl1c.ko" ${PARAMS}
|
|
||||||
fi
|
|
@ -1,28 +0,0 @@
|
|||||||
version: 1
|
|
||||||
name: atl1c
|
|
||||||
description: "Driver for Atheros L1C Gigabit Ethernet adapters"
|
|
||||||
available-for:
|
|
||||||
bromolow-3.10.108:
|
|
||||||
install-script: &script "install.sh"
|
|
||||||
modules: true
|
|
||||||
apollolake-4.4.180:
|
|
||||||
install-script: *script
|
|
||||||
modules: true
|
|
||||||
broadwell-4.4.180:
|
|
||||||
install-script: *script
|
|
||||||
modules: true
|
|
||||||
broadwellnk-4.4.180:
|
|
||||||
install-script: *script
|
|
||||||
modules: true
|
|
||||||
denverton-4.4.180:
|
|
||||||
install-script: *script
|
|
||||||
modules: true
|
|
||||||
geminilake-4.4.180:
|
|
||||||
install-script: *script
|
|
||||||
modules: true
|
|
||||||
v1000-4.4.180:
|
|
||||||
install-script: *script
|
|
||||||
modules: true
|
|
||||||
purley-4.4.180:
|
|
||||||
install-script: *script
|
|
||||||
modules: true
|
|
@ -1,2 +0,0 @@
|
|||||||
obj-m += atl1c.o
|
|
||||||
atl1c-objs := atl1c_main.o atl1c_hw.o atl1c_ethtool.o
|
|
@ -1,606 +0,0 @@
|
|||||||
/*
|
|
||||||
* Copyright(c) 2008 - 2009 Atheros Corporation. All rights reserved.
|
|
||||||
*
|
|
||||||
* Derived from Intel e1000 driver
|
|
||||||
* Copyright(c) 1999 - 2005 Intel Corporation. All rights reserved.
|
|
||||||
*
|
|
||||||
* This program is free software; you can redistribute it and/or modify it
|
|
||||||
* under the terms of the GNU General Public License as published by the Free
|
|
||||||
* Software Foundation; either version 2 of the License, or (at your option)
|
|
||||||
* any later version.
|
|
||||||
*
|
|
||||||
* This program is distributed in the hope that it will be useful, but WITHOUT
|
|
||||||
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
|
|
||||||
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
|
|
||||||
* more details.
|
|
||||||
*
|
|
||||||
* You should have received a copy of the GNU General Public License along with
|
|
||||||
* this program; if not, write to the Free Software Foundation, Inc., 59
|
|
||||||
* Temple Place - Suite 330, Boston, MA 02111-1307, USA.
|
|
||||||
*/
|
|
||||||
|
|
||||||
#ifndef _ATL1C_H_
|
|
||||||
#define _ATL1C_H_
|
|
||||||
|
|
||||||
#include <linux/init.h>
|
|
||||||
#include <linux/interrupt.h>
|
|
||||||
#include <linux/types.h>
|
|
||||||
#include <linux/errno.h>
|
|
||||||
#include <linux/module.h>
|
|
||||||
#include <linux/pci.h>
|
|
||||||
#include <linux/netdevice.h>
|
|
||||||
#include <linux/etherdevice.h>
|
|
||||||
#include <linux/skbuff.h>
|
|
||||||
#include <linux/ioport.h>
|
|
||||||
#include <linux/slab.h>
|
|
||||||
#include <linux/list.h>
|
|
||||||
#include <linux/delay.h>
|
|
||||||
#include <linux/sched.h>
|
|
||||||
#include <linux/in.h>
|
|
||||||
#include <linux/ip.h>
|
|
||||||
#include <linux/ipv6.h>
|
|
||||||
#include <linux/udp.h>
|
|
||||||
#include <linux/mii.h>
|
|
||||||
#include <linux/io.h>
|
|
||||||
#include <linux/vmalloc.h>
|
|
||||||
#include <linux/pagemap.h>
|
|
||||||
#include <linux/tcp.h>
|
|
||||||
#include <linux/ethtool.h>
|
|
||||||
#include <linux/if_vlan.h>
|
|
||||||
#include <linux/workqueue.h>
|
|
||||||
#include <net/checksum.h>
|
|
||||||
#include <net/ip6_checksum.h>
|
|
||||||
|
|
||||||
#include "atl1c_hw.h"
|
|
||||||
|
|
||||||
/* Wake Up Filter Control */
|
|
||||||
#define AT_WUFC_LNKC 0x00000001 /* Link Status Change Wakeup Enable */
|
|
||||||
#define AT_WUFC_MAG 0x00000002 /* Magic Packet Wakeup Enable */
|
|
||||||
#define AT_WUFC_EX 0x00000004 /* Directed Exact Wakeup Enable */
|
|
||||||
#define AT_WUFC_MC 0x00000008 /* Multicast Wakeup Enable */
|
|
||||||
#define AT_WUFC_BC 0x00000010 /* Broadcast Wakeup Enable */
|
|
||||||
|
|
||||||
#define AT_VLAN_TO_TAG(_vlan, _tag) \
|
|
||||||
_tag = ((((_vlan) >> 8) & 0xFF) |\
|
|
||||||
(((_vlan) & 0xFF) << 8))
|
|
||||||
|
|
||||||
#define AT_TAG_TO_VLAN(_tag, _vlan) \
|
|
||||||
_vlan = ((((_tag) >> 8) & 0xFF) |\
|
|
||||||
(((_tag) & 0xFF) << 8))
|
|
||||||
|
|
||||||
#define SPEED_0 0xffff
|
|
||||||
#define HALF_DUPLEX 1
|
|
||||||
#define FULL_DUPLEX 2
|
|
||||||
|
|
||||||
#define AT_RX_BUF_SIZE (ETH_FRAME_LEN + VLAN_HLEN + ETH_FCS_LEN)
|
|
||||||
#define MAX_JUMBO_FRAME_SIZE (6*1024)
|
|
||||||
|
|
||||||
#define AT_MAX_RECEIVE_QUEUE 4
|
|
||||||
#define AT_DEF_RECEIVE_QUEUE 1
|
|
||||||
#define AT_MAX_TRANSMIT_QUEUE 2
|
|
||||||
|
|
||||||
#define AT_DMA_HI_ADDR_MASK 0xffffffff00000000ULL
|
|
||||||
#define AT_DMA_LO_ADDR_MASK 0x00000000ffffffffULL
|
|
||||||
|
|
||||||
#define AT_TX_WATCHDOG (5 * HZ)
|
|
||||||
#define AT_MAX_INT_WORK 5
|
|
||||||
#define AT_TWSI_EEPROM_TIMEOUT 100
|
|
||||||
#define AT_HW_MAX_IDLE_DELAY 10
|
|
||||||
#define AT_SUSPEND_LINK_TIMEOUT 100
|
|
||||||
|
|
||||||
#define AT_ASPM_L0S_TIMER 6
|
|
||||||
#define AT_ASPM_L1_TIMER 12
|
|
||||||
#define AT_LCKDET_TIMER 12
|
|
||||||
|
|
||||||
#define ATL1C_PCIE_L0S_L1_DISABLE 0x01
|
|
||||||
#define ATL1C_PCIE_PHY_RESET 0x02
|
|
||||||
|
|
||||||
#define ATL1C_ASPM_L0s_ENABLE 0x0001
|
|
||||||
#define ATL1C_ASPM_L1_ENABLE 0x0002
|
|
||||||
|
|
||||||
#define AT_REGS_LEN (74 * sizeof(u32))
|
|
||||||
#define AT_EEPROM_LEN 512
|
|
||||||
|
|
||||||
#define ATL1C_GET_DESC(R, i, type) (&(((type *)((R)->desc))[i]))
|
|
||||||
#define ATL1C_RFD_DESC(R, i) ATL1C_GET_DESC(R, i, struct atl1c_rx_free_desc)
|
|
||||||
#define ATL1C_TPD_DESC(R, i) ATL1C_GET_DESC(R, i, struct atl1c_tpd_desc)
|
|
||||||
#define ATL1C_RRD_DESC(R, i) ATL1C_GET_DESC(R, i, struct atl1c_recv_ret_status)
|
|
||||||
|
|
||||||
/* tpd word 1 bit 0:7 General Checksum task offload */
|
|
||||||
#define TPD_L4HDR_OFFSET_MASK 0x00FF
|
|
||||||
#define TPD_L4HDR_OFFSET_SHIFT 0
|
|
||||||
|
|
||||||
/* tpd word 1 bit 0:7 Large Send task offload (IPv4/IPV6) */
|
|
||||||
#define TPD_TCPHDR_OFFSET_MASK 0x00FF
|
|
||||||
#define TPD_TCPHDR_OFFSET_SHIFT 0
|
|
||||||
|
|
||||||
/* tpd word 1 bit 0:7 Custom Checksum task offload */
|
|
||||||
#define TPD_PLOADOFFSET_MASK 0x00FF
|
|
||||||
#define TPD_PLOADOFFSET_SHIFT 0
|
|
||||||
|
|
||||||
/* tpd word 1 bit 8:17 */
|
|
||||||
#define TPD_CCSUM_EN_MASK 0x0001
|
|
||||||
#define TPD_CCSUM_EN_SHIFT 8
|
|
||||||
#define TPD_IP_CSUM_MASK 0x0001
|
|
||||||
#define TPD_IP_CSUM_SHIFT 9
|
|
||||||
#define TPD_TCP_CSUM_MASK 0x0001
|
|
||||||
#define TPD_TCP_CSUM_SHIFT 10
|
|
||||||
#define TPD_UDP_CSUM_MASK 0x0001
|
|
||||||
#define TPD_UDP_CSUM_SHIFT 11
|
|
||||||
#define TPD_LSO_EN_MASK 0x0001 /* TCP Large Send Offload */
|
|
||||||
#define TPD_LSO_EN_SHIFT 12
|
|
||||||
#define TPD_LSO_VER_MASK 0x0001
|
|
||||||
#define TPD_LSO_VER_SHIFT 13 /* 0 : ipv4; 1 : ipv4/ipv6 */
|
|
||||||
#define TPD_CON_VTAG_MASK 0x0001
|
|
||||||
#define TPD_CON_VTAG_SHIFT 14
|
|
||||||
#define TPD_INS_VTAG_MASK 0x0001
|
|
||||||
#define TPD_INS_VTAG_SHIFT 15
|
|
||||||
#define TPD_IPV4_PACKET_MASK 0x0001 /* valid when LSO VER is 1 */
|
|
||||||
#define TPD_IPV4_PACKET_SHIFT 16
|
|
||||||
#define TPD_ETH_TYPE_MASK 0x0001
|
|
||||||
#define TPD_ETH_TYPE_SHIFT 17 /* 0 : 802.3 frame; 1 : Ethernet */
|
|
||||||
|
|
||||||
/* tpd word 18:25 Custom Checksum task offload */
|
|
||||||
#define TPD_CCSUM_OFFSET_MASK 0x00FF
|
|
||||||
#define TPD_CCSUM_OFFSET_SHIFT 18
|
|
||||||
#define TPD_CCSUM_EPAD_MASK 0x0001
|
|
||||||
#define TPD_CCSUM_EPAD_SHIFT 30
|
|
||||||
|
|
||||||
/* tpd word 18:30 Large Send task offload (IPv4/IPV6) */
|
|
||||||
#define TPD_MSS_MASK 0x1FFF
|
|
||||||
#define TPD_MSS_SHIFT 18
|
|
||||||
|
|
||||||
#define TPD_EOP_MASK 0x0001
|
|
||||||
#define TPD_EOP_SHIFT 31
|
|
||||||
|
|
||||||
struct atl1c_tpd_desc {
|
|
||||||
__le16 buffer_len; /* include 4-byte CRC */
|
|
||||||
__le16 vlan_tag;
|
|
||||||
__le32 word1;
|
|
||||||
__le64 buffer_addr;
|
|
||||||
};
|
|
||||||
|
|
||||||
struct atl1c_tpd_ext_desc {
|
|
||||||
u32 reservd_0;
|
|
||||||
__le32 word1;
|
|
||||||
__le32 pkt_len;
|
|
||||||
u32 reservd_1;
|
|
||||||
};
|
|
||||||
/* rrs word 0 bit 0:31 */
|
|
||||||
#define RRS_RX_CSUM_MASK 0xFFFF
|
|
||||||
#define RRS_RX_CSUM_SHIFT 0
|
|
||||||
#define RRS_RX_RFD_CNT_MASK 0x000F
|
|
||||||
#define RRS_RX_RFD_CNT_SHIFT 16
|
|
||||||
#define RRS_RX_RFD_INDEX_MASK 0x0FFF
|
|
||||||
#define RRS_RX_RFD_INDEX_SHIFT 20
|
|
||||||
|
|
||||||
/* rrs flag bit 0:16 */
|
|
||||||
#define RRS_HEAD_LEN_MASK 0x00FF
|
|
||||||
#define RRS_HEAD_LEN_SHIFT 0
|
|
||||||
#define RRS_HDS_TYPE_MASK 0x0003
|
|
||||||
#define RRS_HDS_TYPE_SHIFT 8
|
|
||||||
#define RRS_CPU_NUM_MASK 0x0003
|
|
||||||
#define RRS_CPU_NUM_SHIFT 10
|
|
||||||
#define RRS_HASH_FLG_MASK 0x000F
|
|
||||||
#define RRS_HASH_FLG_SHIFT 12
|
|
||||||
|
|
||||||
#define RRS_HDS_TYPE_HEAD 1
|
|
||||||
#define RRS_HDS_TYPE_DATA 2
|
|
||||||
|
|
||||||
#define RRS_IS_NO_HDS_TYPE(flag) \
|
|
||||||
((((flag) >> (RRS_HDS_TYPE_SHIFT)) & RRS_HDS_TYPE_MASK) == 0)
|
|
||||||
|
|
||||||
#define RRS_IS_HDS_HEAD(flag) \
|
|
||||||
((((flag) >> (RRS_HDS_TYPE_SHIFT)) & RRS_HDS_TYPE_MASK) == \
|
|
||||||
RRS_HDS_TYPE_HEAD)
|
|
||||||
|
|
||||||
#define RRS_IS_HDS_DATA(flag) \
|
|
||||||
((((flag) >> (RRS_HDS_TYPE_SHIFT)) & RRS_HDS_TYPE_MASK) == \
|
|
||||||
RRS_HDS_TYPE_DATA)
|
|
||||||
|
|
||||||
/* rrs word 3 bit 0:31 */
|
|
||||||
#define RRS_PKT_SIZE_MASK 0x3FFF
|
|
||||||
#define RRS_PKT_SIZE_SHIFT 0
|
|
||||||
#define RRS_ERR_L4_CSUM_MASK 0x0001
|
|
||||||
#define RRS_ERR_L4_CSUM_SHIFT 14
|
|
||||||
#define RRS_ERR_IP_CSUM_MASK 0x0001
|
|
||||||
#define RRS_ERR_IP_CSUM_SHIFT 15
|
|
||||||
#define RRS_VLAN_INS_MASK 0x0001
|
|
||||||
#define RRS_VLAN_INS_SHIFT 16
|
|
||||||
#define RRS_PROT_ID_MASK 0x0007
|
|
||||||
#define RRS_PROT_ID_SHIFT 17
|
|
||||||
#define RRS_RX_ERR_SUM_MASK 0x0001
|
|
||||||
#define RRS_RX_ERR_SUM_SHIFT 20
|
|
||||||
#define RRS_RX_ERR_CRC_MASK 0x0001
|
|
||||||
#define RRS_RX_ERR_CRC_SHIFT 21
|
|
||||||
#define RRS_RX_ERR_FAE_MASK 0x0001
|
|
||||||
#define RRS_RX_ERR_FAE_SHIFT 22
|
|
||||||
#define RRS_RX_ERR_TRUNC_MASK 0x0001
|
|
||||||
#define RRS_RX_ERR_TRUNC_SHIFT 23
|
|
||||||
#define RRS_RX_ERR_RUNC_MASK 0x0001
|
|
||||||
#define RRS_RX_ERR_RUNC_SHIFT 24
|
|
||||||
#define RRS_RX_ERR_ICMP_MASK 0x0001
|
|
||||||
#define RRS_RX_ERR_ICMP_SHIFT 25
|
|
||||||
#define RRS_PACKET_BCAST_MASK 0x0001
|
|
||||||
#define RRS_PACKET_BCAST_SHIFT 26
|
|
||||||
#define RRS_PACKET_MCAST_MASK 0x0001
|
|
||||||
#define RRS_PACKET_MCAST_SHIFT 27
|
|
||||||
#define RRS_PACKET_TYPE_MASK 0x0001
|
|
||||||
#define RRS_PACKET_TYPE_SHIFT 28
|
|
||||||
#define RRS_FIFO_FULL_MASK 0x0001
|
|
||||||
#define RRS_FIFO_FULL_SHIFT 29
|
|
||||||
#define RRS_802_3_LEN_ERR_MASK 0x0001
|
|
||||||
#define RRS_802_3_LEN_ERR_SHIFT 30
|
|
||||||
#define RRS_RXD_UPDATED_MASK 0x0001
|
|
||||||
#define RRS_RXD_UPDATED_SHIFT 31
|
|
||||||
|
|
||||||
#define RRS_ERR_L4_CSUM 0x00004000
|
|
||||||
#define RRS_ERR_IP_CSUM 0x00008000
|
|
||||||
#define RRS_VLAN_INS 0x00010000
|
|
||||||
#define RRS_RX_ERR_SUM 0x00100000
|
|
||||||
#define RRS_RX_ERR_CRC 0x00200000
|
|
||||||
#define RRS_802_3_LEN_ERR 0x40000000
|
|
||||||
#define RRS_RXD_UPDATED 0x80000000
|
|
||||||
|
|
||||||
#define RRS_PACKET_TYPE_802_3 1
|
|
||||||
#define RRS_PACKET_TYPE_ETH 0
|
|
||||||
#define RRS_PACKET_IS_ETH(word) \
|
|
||||||
((((word) >> RRS_PACKET_TYPE_SHIFT) & RRS_PACKET_TYPE_MASK) == \
|
|
||||||
RRS_PACKET_TYPE_ETH)
|
|
||||||
#define RRS_RXD_IS_VALID(word) \
|
|
||||||
((((word) >> RRS_RXD_UPDATED_SHIFT) & RRS_RXD_UPDATED_MASK) == 1)
|
|
||||||
|
|
||||||
#define RRS_PACKET_PROT_IS_IPV4_ONLY(word) \
|
|
||||||
((((word) >> RRS_PROT_ID_SHIFT) & RRS_PROT_ID_MASK) == 1)
|
|
||||||
#define RRS_PACKET_PROT_IS_IPV6_ONLY(word) \
|
|
||||||
((((word) >> RRS_PROT_ID_SHIFT) & RRS_PROT_ID_MASK) == 6)
|
|
||||||
|
|
||||||
struct atl1c_recv_ret_status {
|
|
||||||
__le32 word0;
|
|
||||||
__le32 rss_hash;
|
|
||||||
__le16 vlan_tag;
|
|
||||||
__le16 flag;
|
|
||||||
__le32 word3;
|
|
||||||
};
|
|
||||||
|
|
||||||
/* RFD descriptor */
|
|
||||||
struct atl1c_rx_free_desc {
|
|
||||||
__le64 buffer_addr;
|
|
||||||
};
|
|
||||||
|
|
||||||
/* DMA Order Settings */
|
|
||||||
enum atl1c_dma_order {
|
|
||||||
atl1c_dma_ord_in = 1,
|
|
||||||
atl1c_dma_ord_enh = 2,
|
|
||||||
atl1c_dma_ord_out = 4
|
|
||||||
};
|
|
||||||
|
|
||||||
enum atl1c_dma_rcb {
|
|
||||||
atl1c_rcb_64 = 0,
|
|
||||||
atl1c_rcb_128 = 1
|
|
||||||
};
|
|
||||||
|
|
||||||
enum atl1c_mac_speed {
|
|
||||||
atl1c_mac_speed_0 = 0,
|
|
||||||
atl1c_mac_speed_10_100 = 1,
|
|
||||||
atl1c_mac_speed_1000 = 2
|
|
||||||
};
|
|
||||||
|
|
||||||
enum atl1c_dma_req_block {
|
|
||||||
atl1c_dma_req_128 = 0,
|
|
||||||
atl1c_dma_req_256 = 1,
|
|
||||||
atl1c_dma_req_512 = 2,
|
|
||||||
atl1c_dma_req_1024 = 3,
|
|
||||||
atl1c_dma_req_2048 = 4,
|
|
||||||
atl1c_dma_req_4096 = 5
|
|
||||||
};
|
|
||||||
|
|
||||||
|
|
||||||
enum atl1c_nic_type {
|
|
||||||
athr_l1c = 0,
|
|
||||||
athr_l2c = 1,
|
|
||||||
athr_l2c_b,
|
|
||||||
athr_l2c_b2,
|
|
||||||
athr_l1d,
|
|
||||||
athr_l1d_2,
|
|
||||||
};
|
|
||||||
|
|
||||||
enum atl1c_trans_queue {
|
|
||||||
atl1c_trans_normal = 0,
|
|
||||||
atl1c_trans_high = 1
|
|
||||||
};
|
|
||||||
|
|
||||||
struct atl1c_hw_stats {
|
|
||||||
/* rx */
|
|
||||||
unsigned long rx_ok; /* The number of good packet received. */
|
|
||||||
unsigned long rx_bcast; /* The number of good broadcast packet received. */
|
|
||||||
unsigned long rx_mcast; /* The number of good multicast packet received. */
|
|
||||||
unsigned long rx_pause; /* The number of Pause packet received. */
|
|
||||||
unsigned long rx_ctrl; /* The number of Control packet received other than Pause frame. */
|
|
||||||
unsigned long rx_fcs_err; /* The number of packets with bad FCS. */
|
|
||||||
unsigned long rx_len_err; /* The number of packets with mismatch of length field and actual size. */
|
|
||||||
unsigned long rx_byte_cnt; /* The number of bytes of good packet received. FCS is NOT included. */
|
|
||||||
unsigned long rx_runt; /* The number of packets received that are less than 64 byte long and with good FCS. */
|
|
||||||
unsigned long rx_frag; /* The number of packets received that are less than 64 byte long and with bad FCS. */
|
|
||||||
unsigned long rx_sz_64; /* The number of good and bad packets received that are 64 byte long. */
|
|
||||||
unsigned long rx_sz_65_127; /* The number of good and bad packets received that are between 65 and 127-byte long. */
|
|
||||||
unsigned long rx_sz_128_255; /* The number of good and bad packets received that are between 128 and 255-byte long. */
|
|
||||||
unsigned long rx_sz_256_511; /* The number of good and bad packets received that are between 256 and 511-byte long. */
|
|
||||||
unsigned long rx_sz_512_1023; /* The number of good and bad packets received that are between 512 and 1023-byte long. */
|
|
||||||
unsigned long rx_sz_1024_1518; /* The number of good and bad packets received that are between 1024 and 1518-byte long. */
|
|
||||||
unsigned long rx_sz_1519_max; /* The number of good and bad packets received that are between 1519-byte and MTU. */
|
|
||||||
unsigned long rx_sz_ov; /* The number of good and bad packets received that are more than MTU size truncated by Selene. */
|
|
||||||
unsigned long rx_rxf_ov; /* The number of frame dropped due to occurrence of RX FIFO overflow. */
|
|
||||||
unsigned long rx_rrd_ov; /* The number of frame dropped due to occurrence of RRD overflow. */
|
|
||||||
unsigned long rx_align_err; /* Alignment Error */
|
|
||||||
unsigned long rx_bcast_byte_cnt; /* The byte count of broadcast packet received, excluding FCS. */
|
|
||||||
unsigned long rx_mcast_byte_cnt; /* The byte count of multicast packet received, excluding FCS. */
|
|
||||||
unsigned long rx_err_addr; /* The number of packets dropped due to address filtering. */
|
|
||||||
|
|
||||||
/* tx */
|
|
||||||
unsigned long tx_ok; /* The number of good packet transmitted. */
|
|
||||||
unsigned long tx_bcast; /* The number of good broadcast packet transmitted. */
|
|
||||||
unsigned long tx_mcast; /* The number of good multicast packet transmitted. */
|
|
||||||
unsigned long tx_pause; /* The number of Pause packet transmitted. */
|
|
||||||
unsigned long tx_exc_defer; /* The number of packets transmitted with excessive deferral. */
|
|
||||||
unsigned long tx_ctrl; /* The number of packets transmitted is a control frame, excluding Pause frame. */
|
|
||||||
unsigned long tx_defer; /* The number of packets transmitted that is deferred. */
|
|
||||||
unsigned long tx_byte_cnt; /* The number of bytes of data transmitted. FCS is NOT included. */
|
|
||||||
unsigned long tx_sz_64; /* The number of good and bad packets transmitted that are 64 byte long. */
|
|
||||||
unsigned long tx_sz_65_127; /* The number of good and bad packets transmitted that are between 65 and 127-byte long. */
|
|
||||||
unsigned long tx_sz_128_255; /* The number of good and bad packets transmitted that are between 128 and 255-byte long. */
|
|
||||||
unsigned long tx_sz_256_511; /* The number of good and bad packets transmitted that are between 256 and 511-byte long. */
|
|
||||||
unsigned long tx_sz_512_1023; /* The number of good and bad packets transmitted that are between 512 and 1023-byte long. */
|
|
||||||
unsigned long tx_sz_1024_1518; /* The number of good and bad packets transmitted that are between 1024 and 1518-byte long. */
|
|
||||||
unsigned long tx_sz_1519_max; /* The number of good and bad packets transmitted that are between 1519-byte and MTU. */
|
|
||||||
unsigned long tx_1_col; /* The number of packets subsequently transmitted successfully with a single prior collision. */
|
|
||||||
unsigned long tx_2_col; /* The number of packets subsequently transmitted successfully with multiple prior collisions. */
|
|
||||||
unsigned long tx_late_col; /* The number of packets transmitted with late collisions. */
|
|
||||||
unsigned long tx_abort_col; /* The number of transmit packets aborted due to excessive collisions. */
|
|
||||||
unsigned long tx_underrun; /* The number of transmit packets aborted due to transmit FIFO underrun, or TRD FIFO underrun */
|
|
||||||
unsigned long tx_rd_eop; /* The number of times that read beyond the EOP into the next frame area when TRD was not written timely */
|
|
||||||
unsigned long tx_len_err; /* The number of transmit packets with length field does NOT match the actual frame size. */
|
|
||||||
unsigned long tx_trunc; /* The number of transmit packets truncated due to size exceeding MTU. */
|
|
||||||
unsigned long tx_bcast_byte; /* The byte count of broadcast packet transmitted, excluding FCS. */
|
|
||||||
unsigned long tx_mcast_byte; /* The byte count of multicast packet transmitted, excluding FCS. */
|
|
||||||
};
|
|
||||||
|
|
||||||
struct atl1c_hw {
|
|
||||||
u8 __iomem *hw_addr; /* inner register address */
|
|
||||||
struct atl1c_adapter *adapter;
|
|
||||||
enum atl1c_nic_type nic_type;
|
|
||||||
enum atl1c_dma_order dma_order;
|
|
||||||
enum atl1c_dma_rcb rcb_value;
|
|
||||||
enum atl1c_dma_req_block dmar_block;
|
|
||||||
|
|
||||||
u16 device_id;
|
|
||||||
u16 vendor_id;
|
|
||||||
u16 subsystem_id;
|
|
||||||
u16 subsystem_vendor_id;
|
|
||||||
u8 revision_id;
|
|
||||||
u16 phy_id1;
|
|
||||||
u16 phy_id2;
|
|
||||||
|
|
||||||
u32 intr_mask;
|
|
||||||
|
|
||||||
u8 preamble_len;
|
|
||||||
u16 max_frame_size;
|
|
||||||
u16 min_frame_size;
|
|
||||||
|
|
||||||
enum atl1c_mac_speed mac_speed;
|
|
||||||
bool mac_duplex;
|
|
||||||
bool hibernate;
|
|
||||||
u16 media_type;
|
|
||||||
#define MEDIA_TYPE_AUTO_SENSOR 0
|
|
||||||
#define MEDIA_TYPE_100M_FULL 1
|
|
||||||
#define MEDIA_TYPE_100M_HALF 2
|
|
||||||
#define MEDIA_TYPE_10M_FULL 3
|
|
||||||
#define MEDIA_TYPE_10M_HALF 4
|
|
||||||
|
|
||||||
u16 autoneg_advertised;
|
|
||||||
u16 mii_autoneg_adv_reg;
|
|
||||||
u16 mii_1000t_ctrl_reg;
|
|
||||||
|
|
||||||
u16 tx_imt; /* TX Interrupt Moderator timer ( 2us resolution) */
|
|
||||||
u16 rx_imt; /* RX Interrupt Moderator timer ( 2us resolution) */
|
|
||||||
u16 ict; /* Interrupt Clear timer (2us resolution) */
|
|
||||||
u16 ctrl_flags;
|
|
||||||
#define ATL1C_INTR_CLEAR_ON_READ 0x0001
|
|
||||||
#define ATL1C_INTR_MODRT_ENABLE 0x0002
|
|
||||||
#define ATL1C_CMB_ENABLE 0x0004
|
|
||||||
#define ATL1C_SMB_ENABLE 0x0010
|
|
||||||
#define ATL1C_TXQ_MODE_ENHANCE 0x0020
|
|
||||||
#define ATL1C_RX_IPV6_CHKSUM 0x0040
|
|
||||||
#define ATL1C_ASPM_L0S_SUPPORT 0x0080
|
|
||||||
#define ATL1C_ASPM_L1_SUPPORT 0x0100
|
|
||||||
#define ATL1C_ASPM_CTRL_MON 0x0200
|
|
||||||
#define ATL1C_HIB_DISABLE 0x0400
|
|
||||||
#define ATL1C_APS_MODE_ENABLE 0x0800
|
|
||||||
#define ATL1C_LINK_EXT_SYNC 0x1000
|
|
||||||
#define ATL1C_CLK_GATING_EN 0x2000
|
|
||||||
#define ATL1C_FPGA_VERSION 0x8000
|
|
||||||
u16 link_cap_flags;
|
|
||||||
#define ATL1C_LINK_CAP_1000M 0x0001
|
|
||||||
u32 smb_timer;
|
|
||||||
|
|
||||||
u16 rrd_thresh; /* Threshold of number of RRD produced to trigger
|
|
||||||
interrupt request */
|
|
||||||
u16 tpd_thresh;
|
|
||||||
u8 tpd_burst; /* Number of TPD to prefetch in cache-aligned burst. */
|
|
||||||
u8 rfd_burst;
|
|
||||||
u32 base_cpu;
|
|
||||||
u32 indirect_tab;
|
|
||||||
u8 mac_addr[ETH_ALEN];
|
|
||||||
u8 perm_mac_addr[ETH_ALEN];
|
|
||||||
|
|
||||||
bool phy_configured;
|
|
||||||
bool re_autoneg;
|
|
||||||
bool emi_ca;
|
|
||||||
bool msi_lnkpatch; /* link patch for specific platforms */
|
|
||||||
};
|
|
||||||
|
|
||||||
/*
|
|
||||||
* atl1c_ring_header represents a single, contiguous block of DMA space
|
|
||||||
* mapped for the three descriptor rings (tpd, rfd, rrd) described below
|
|
||||||
*/
|
|
||||||
struct atl1c_ring_header {
|
|
||||||
void *desc; /* virtual address */
|
|
||||||
dma_addr_t dma; /* physical address*/
|
|
||||||
unsigned int size; /* length in bytes */
|
|
||||||
};
|
|
||||||
|
|
||||||
/*
|
|
||||||
* atl1c_buffer is wrapper around a pointer to a socket buffer
|
|
||||||
* so a DMA handle can be stored along with the skb
|
|
||||||
*/
|
|
||||||
struct atl1c_buffer {
|
|
||||||
struct sk_buff *skb; /* socket buffer */
|
|
||||||
u16 length; /* rx buffer length */
|
|
||||||
u16 flags; /* information of buffer */
|
|
||||||
#define ATL1C_BUFFER_FREE 0x0001
|
|
||||||
#define ATL1C_BUFFER_BUSY 0x0002
|
|
||||||
#define ATL1C_BUFFER_STATE_MASK 0x0003
|
|
||||||
|
|
||||||
#define ATL1C_PCIMAP_SINGLE 0x0004
|
|
||||||
#define ATL1C_PCIMAP_PAGE 0x0008
|
|
||||||
#define ATL1C_PCIMAP_TYPE_MASK 0x000C
|
|
||||||
|
|
||||||
#define ATL1C_PCIMAP_TODEVICE 0x0010
|
|
||||||
#define ATL1C_PCIMAP_FROMDEVICE 0x0020
|
|
||||||
#define ATL1C_PCIMAP_DIRECTION_MASK 0x0030
|
|
||||||
dma_addr_t dma;
|
|
||||||
};
|
|
||||||
|
|
||||||
#define ATL1C_SET_BUFFER_STATE(buff, state) do { \
|
|
||||||
((buff)->flags) &= ~ATL1C_BUFFER_STATE_MASK; \
|
|
||||||
((buff)->flags) |= (state); \
|
|
||||||
} while (0)
|
|
||||||
|
|
||||||
#define ATL1C_SET_PCIMAP_TYPE(buff, type, direction) do { \
|
|
||||||
((buff)->flags) &= ~ATL1C_PCIMAP_TYPE_MASK; \
|
|
||||||
((buff)->flags) |= (type); \
|
|
||||||
((buff)->flags) &= ~ATL1C_PCIMAP_DIRECTION_MASK; \
|
|
||||||
((buff)->flags) |= (direction); \
|
|
||||||
} while (0)
|
|
||||||
|
|
||||||
/* transimit packet descriptor (tpd) ring */
|
|
||||||
struct atl1c_tpd_ring {
|
|
||||||
void *desc; /* descriptor ring virtual address */
|
|
||||||
dma_addr_t dma; /* descriptor ring physical address */
|
|
||||||
u16 size; /* descriptor ring length in bytes */
|
|
||||||
u16 count; /* number of descriptors in the ring */
|
|
||||||
u16 next_to_use; /* this is protectd by adapter->tx_lock */
|
|
||||||
atomic_t next_to_clean;
|
|
||||||
struct atl1c_buffer *buffer_info;
|
|
||||||
};
|
|
||||||
|
|
||||||
/* receive free descriptor (rfd) ring */
|
|
||||||
struct atl1c_rfd_ring {
|
|
||||||
void *desc; /* descriptor ring virtual address */
|
|
||||||
dma_addr_t dma; /* descriptor ring physical address */
|
|
||||||
u16 size; /* descriptor ring length in bytes */
|
|
||||||
u16 count; /* number of descriptors in the ring */
|
|
||||||
u16 next_to_use;
|
|
||||||
u16 next_to_clean;
|
|
||||||
struct atl1c_buffer *buffer_info;
|
|
||||||
};
|
|
||||||
|
|
||||||
/* receive return descriptor (rrd) ring */
|
|
||||||
struct atl1c_rrd_ring {
|
|
||||||
void *desc; /* descriptor ring virtual address */
|
|
||||||
dma_addr_t dma; /* descriptor ring physical address */
|
|
||||||
u16 size; /* descriptor ring length in bytes */
|
|
||||||
u16 count; /* number of descriptors in the ring */
|
|
||||||
u16 next_to_use;
|
|
||||||
u16 next_to_clean;
|
|
||||||
};
|
|
||||||
|
|
||||||
/* board specific private data structure */
|
|
||||||
struct atl1c_adapter {
|
|
||||||
struct net_device *netdev;
|
|
||||||
struct pci_dev *pdev;
|
|
||||||
struct napi_struct napi;
|
|
||||||
struct page *rx_page;
|
|
||||||
unsigned int rx_page_offset;
|
|
||||||
unsigned int rx_frag_size;
|
|
||||||
struct atl1c_hw hw;
|
|
||||||
struct atl1c_hw_stats hw_stats;
|
|
||||||
struct mii_if_info mii; /* MII interface info */
|
|
||||||
u16 rx_buffer_len;
|
|
||||||
|
|
||||||
unsigned long flags;
|
|
||||||
#define __AT_TESTING 0x0001
|
|
||||||
#define __AT_RESETTING 0x0002
|
|
||||||
#define __AT_DOWN 0x0003
|
|
||||||
unsigned long work_event;
|
|
||||||
#define ATL1C_WORK_EVENT_RESET 0
|
|
||||||
#define ATL1C_WORK_EVENT_LINK_CHANGE 1
|
|
||||||
u32 msg_enable;
|
|
||||||
|
|
||||||
bool have_msi;
|
|
||||||
u32 wol;
|
|
||||||
u16 link_speed;
|
|
||||||
u16 link_duplex;
|
|
||||||
|
|
||||||
spinlock_t mdio_lock;
|
|
||||||
spinlock_t tx_lock;
|
|
||||||
atomic_t irq_sem;
|
|
||||||
|
|
||||||
struct work_struct common_task;
|
|
||||||
struct timer_list watchdog_timer;
|
|
||||||
struct timer_list phy_config_timer;
|
|
||||||
|
|
||||||
/* All Descriptor memory */
|
|
||||||
struct atl1c_ring_header ring_header;
|
|
||||||
struct atl1c_tpd_ring tpd_ring[AT_MAX_TRANSMIT_QUEUE];
|
|
||||||
struct atl1c_rfd_ring rfd_ring;
|
|
||||||
struct atl1c_rrd_ring rrd_ring;
|
|
||||||
u32 bd_number; /* board number;*/
|
|
||||||
};
|
|
||||||
|
|
||||||
#define AT_WRITE_REG(a, reg, value) ( \
|
|
||||||
writel((value), ((a)->hw_addr + reg)))
|
|
||||||
|
|
||||||
#define AT_WRITE_FLUSH(a) (\
|
|
||||||
readl((a)->hw_addr))
|
|
||||||
|
|
||||||
#define AT_READ_REG(a, reg, pdata) do { \
|
|
||||||
if (unlikely((a)->hibernate)) { \
|
|
||||||
readl((a)->hw_addr + reg); \
|
|
||||||
*(u32 *)pdata = readl((a)->hw_addr + reg); \
|
|
||||||
} else { \
|
|
||||||
*(u32 *)pdata = readl((a)->hw_addr + reg); \
|
|
||||||
} \
|
|
||||||
} while (0)
|
|
||||||
|
|
||||||
#define AT_WRITE_REGB(a, reg, value) (\
|
|
||||||
writeb((value), ((a)->hw_addr + reg)))
|
|
||||||
|
|
||||||
#define AT_READ_REGB(a, reg) (\
|
|
||||||
readb((a)->hw_addr + reg))
|
|
||||||
|
|
||||||
#define AT_WRITE_REGW(a, reg, value) (\
|
|
||||||
writew((value), ((a)->hw_addr + reg)))
|
|
||||||
|
|
||||||
#define AT_READ_REGW(a, reg, pdata) do { \
|
|
||||||
if (unlikely((a)->hibernate)) { \
|
|
||||||
readw((a)->hw_addr + reg); \
|
|
||||||
*(u16 *)pdata = readw((a)->hw_addr + reg); \
|
|
||||||
} else { \
|
|
||||||
*(u16 *)pdata = readw((a)->hw_addr + reg); \
|
|
||||||
} \
|
|
||||||
} while (0)
|
|
||||||
|
|
||||||
#define AT_WRITE_REG_ARRAY(a, reg, offset, value) ( \
|
|
||||||
writel((value), (((a)->hw_addr + reg) + ((offset) << 2))))
|
|
||||||
|
|
||||||
#define AT_READ_REG_ARRAY(a, reg, offset) ( \
|
|
||||||
readl(((a)->hw_addr + reg) + ((offset) << 2)))
|
|
||||||
|
|
||||||
extern char atl1c_driver_name[];
|
|
||||||
extern char atl1c_driver_version[];
|
|
||||||
|
|
||||||
extern void atl1c_reinit_locked(struct atl1c_adapter *adapter);
|
|
||||||
extern s32 atl1c_reset_hw(struct atl1c_hw *hw);
|
|
||||||
extern void atl1c_set_ethtool_ops(struct net_device *netdev);
|
|
||||||
#endif /* _ATL1C_H_ */
|
|
@ -1,309 +0,0 @@
|
|||||||
/*
|
|
||||||
* Copyright(c) 2009 - 2009 Atheros Corporation. All rights reserved.
|
|
||||||
*
|
|
||||||
* Derived from Intel e1000 driver
|
|
||||||
* Copyright(c) 1999 - 2005 Intel Corporation. All rights reserved.
|
|
||||||
*
|
|
||||||
* This program is free software; you can redistribute it and/or modify it
|
|
||||||
* under the terms of the GNU General Public License as published by the Free
|
|
||||||
* Software Foundation; either version 2 of the License, or (at your option)
|
|
||||||
* any later version.
|
|
||||||
*
|
|
||||||
* This program is distributed in the hope that it will be useful, but WITHOUT
|
|
||||||
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
|
|
||||||
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
|
|
||||||
* more details.
|
|
||||||
*
|
|
||||||
* You should have received a copy of the GNU General Public License along with
|
|
||||||
* this program; if not, write to the Free Software Foundation, Inc., 59
|
|
||||||
* Temple Place - Suite 330, Boston, MA 02111-1307, USA.
|
|
||||||
*
|
|
||||||
*/
|
|
||||||
|
|
||||||
#include <linux/netdevice.h>
|
|
||||||
#include <linux/ethtool.h>
|
|
||||||
#include <linux/slab.h>
|
|
||||||
|
|
||||||
#include "atl1c.h"
|
|
||||||
|
|
||||||
static int atl1c_get_settings(struct net_device *netdev,
|
|
||||||
struct ethtool_cmd *ecmd)
|
|
||||||
{
|
|
||||||
struct atl1c_adapter *adapter = netdev_priv(netdev);
|
|
||||||
struct atl1c_hw *hw = &adapter->hw;
|
|
||||||
|
|
||||||
ecmd->supported = (SUPPORTED_10baseT_Half |
|
|
||||||
SUPPORTED_10baseT_Full |
|
|
||||||
SUPPORTED_100baseT_Half |
|
|
||||||
SUPPORTED_100baseT_Full |
|
|
||||||
SUPPORTED_Autoneg |
|
|
||||||
SUPPORTED_TP);
|
|
||||||
if (hw->link_cap_flags & ATL1C_LINK_CAP_1000M)
|
|
||||||
ecmd->supported |= SUPPORTED_1000baseT_Full;
|
|
||||||
|
|
||||||
ecmd->advertising = ADVERTISED_TP;
|
|
||||||
|
|
||||||
ecmd->advertising |= hw->autoneg_advertised;
|
|
||||||
|
|
||||||
ecmd->port = PORT_TP;
|
|
||||||
ecmd->phy_address = 0;
|
|
||||||
ecmd->transceiver = XCVR_INTERNAL;
|
|
||||||
|
|
||||||
if (adapter->link_speed != SPEED_0) {
|
|
||||||
ethtool_cmd_speed_set(ecmd, adapter->link_speed);
|
|
||||||
if (adapter->link_duplex == FULL_DUPLEX)
|
|
||||||
ecmd->duplex = DUPLEX_FULL;
|
|
||||||
else
|
|
||||||
ecmd->duplex = DUPLEX_HALF;
|
|
||||||
} else {
|
|
||||||
ethtool_cmd_speed_set(ecmd, -1);
|
|
||||||
ecmd->duplex = -1;
|
|
||||||
}
|
|
||||||
|
|
||||||
ecmd->autoneg = AUTONEG_ENABLE;
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
static int atl1c_set_settings(struct net_device *netdev,
|
|
||||||
struct ethtool_cmd *ecmd)
|
|
||||||
{
|
|
||||||
struct atl1c_adapter *adapter = netdev_priv(netdev);
|
|
||||||
struct atl1c_hw *hw = &adapter->hw;
|
|
||||||
u16 autoneg_advertised;
|
|
||||||
|
|
||||||
while (test_and_set_bit(__AT_RESETTING, &adapter->flags))
|
|
||||||
msleep(1);
|
|
||||||
|
|
||||||
if (ecmd->autoneg == AUTONEG_ENABLE) {
|
|
||||||
autoneg_advertised = ADVERTISED_Autoneg;
|
|
||||||
} else {
|
|
||||||
u32 speed = ethtool_cmd_speed(ecmd);
|
|
||||||
if (speed == SPEED_1000) {
|
|
||||||
if (ecmd->duplex != DUPLEX_FULL) {
|
|
||||||
if (netif_msg_link(adapter))
|
|
||||||
dev_warn(&adapter->pdev->dev,
|
|
||||||
"1000M half is invalid\n");
|
|
||||||
clear_bit(__AT_RESETTING, &adapter->flags);
|
|
||||||
return -EINVAL;
|
|
||||||
}
|
|
||||||
autoneg_advertised = ADVERTISED_1000baseT_Full;
|
|
||||||
} else if (speed == SPEED_100) {
|
|
||||||
if (ecmd->duplex == DUPLEX_FULL)
|
|
||||||
autoneg_advertised = ADVERTISED_100baseT_Full;
|
|
||||||
else
|
|
||||||
autoneg_advertised = ADVERTISED_100baseT_Half;
|
|
||||||
} else {
|
|
||||||
if (ecmd->duplex == DUPLEX_FULL)
|
|
||||||
autoneg_advertised = ADVERTISED_10baseT_Full;
|
|
||||||
else
|
|
||||||
autoneg_advertised = ADVERTISED_10baseT_Half;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if (hw->autoneg_advertised != autoneg_advertised) {
|
|
||||||
hw->autoneg_advertised = autoneg_advertised;
|
|
||||||
if (atl1c_restart_autoneg(hw) != 0) {
|
|
||||||
if (netif_msg_link(adapter))
|
|
||||||
dev_warn(&adapter->pdev->dev,
|
|
||||||
"ethtool speed/duplex setting failed\n");
|
|
||||||
clear_bit(__AT_RESETTING, &adapter->flags);
|
|
||||||
return -EINVAL;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
clear_bit(__AT_RESETTING, &adapter->flags);
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
static u32 atl1c_get_msglevel(struct net_device *netdev)
|
|
||||||
{
|
|
||||||
struct atl1c_adapter *adapter = netdev_priv(netdev);
|
|
||||||
return adapter->msg_enable;
|
|
||||||
}
|
|
||||||
|
|
||||||
static void atl1c_set_msglevel(struct net_device *netdev, u32 data)
|
|
||||||
{
|
|
||||||
struct atl1c_adapter *adapter = netdev_priv(netdev);
|
|
||||||
adapter->msg_enable = data;
|
|
||||||
}
|
|
||||||
|
|
||||||
static int atl1c_get_regs_len(struct net_device *netdev)
|
|
||||||
{
|
|
||||||
return AT_REGS_LEN;
|
|
||||||
}
|
|
||||||
|
|
||||||
static void atl1c_get_regs(struct net_device *netdev,
|
|
||||||
struct ethtool_regs *regs, void *p)
|
|
||||||
{
|
|
||||||
struct atl1c_adapter *adapter = netdev_priv(netdev);
|
|
||||||
struct atl1c_hw *hw = &adapter->hw;
|
|
||||||
u32 *regs_buff = p;
|
|
||||||
u16 phy_data;
|
|
||||||
|
|
||||||
memset(p, 0, AT_REGS_LEN);
|
|
||||||
|
|
||||||
regs->version = 1;
|
|
||||||
AT_READ_REG(hw, REG_PM_CTRL, p++);
|
|
||||||
AT_READ_REG(hw, REG_MAC_HALF_DUPLX_CTRL, p++);
|
|
||||||
AT_READ_REG(hw, REG_TWSI_CTRL, p++);
|
|
||||||
AT_READ_REG(hw, REG_PCIE_DEV_MISC_CTRL, p++);
|
|
||||||
AT_READ_REG(hw, REG_MASTER_CTRL, p++);
|
|
||||||
AT_READ_REG(hw, REG_MANUAL_TIMER_INIT, p++);
|
|
||||||
AT_READ_REG(hw, REG_IRQ_MODRT_TIMER_INIT, p++);
|
|
||||||
AT_READ_REG(hw, REG_GPHY_CTRL, p++);
|
|
||||||
AT_READ_REG(hw, REG_LINK_CTRL, p++);
|
|
||||||
AT_READ_REG(hw, REG_IDLE_STATUS, p++);
|
|
||||||
AT_READ_REG(hw, REG_MDIO_CTRL, p++);
|
|
||||||
AT_READ_REG(hw, REG_SERDES, p++);
|
|
||||||
AT_READ_REG(hw, REG_MAC_CTRL, p++);
|
|
||||||
AT_READ_REG(hw, REG_MAC_IPG_IFG, p++);
|
|
||||||
AT_READ_REG(hw, REG_MAC_STA_ADDR, p++);
|
|
||||||
AT_READ_REG(hw, REG_MAC_STA_ADDR+4, p++);
|
|
||||||
AT_READ_REG(hw, REG_RX_HASH_TABLE, p++);
|
|
||||||
AT_READ_REG(hw, REG_RX_HASH_TABLE+4, p++);
|
|
||||||
AT_READ_REG(hw, REG_RXQ_CTRL, p++);
|
|
||||||
AT_READ_REG(hw, REG_TXQ_CTRL, p++);
|
|
||||||
AT_READ_REG(hw, REG_MTU, p++);
|
|
||||||
AT_READ_REG(hw, REG_WOL_CTRL, p++);
|
|
||||||
|
|
||||||
atl1c_read_phy_reg(hw, MII_BMCR, &phy_data);
|
|
||||||
regs_buff[AT_REGS_LEN/sizeof(u32) - 2] = (u32) phy_data;
|
|
||||||
atl1c_read_phy_reg(hw, MII_BMSR, &phy_data);
|
|
||||||
regs_buff[AT_REGS_LEN/sizeof(u32) - 1] = (u32) phy_data;
|
|
||||||
}
|
|
||||||
|
|
||||||
static int atl1c_get_eeprom_len(struct net_device *netdev)
|
|
||||||
{
|
|
||||||
struct atl1c_adapter *adapter = netdev_priv(netdev);
|
|
||||||
|
|
||||||
if (atl1c_check_eeprom_exist(&adapter->hw))
|
|
||||||
return AT_EEPROM_LEN;
|
|
||||||
else
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
static int atl1c_get_eeprom(struct net_device *netdev,
|
|
||||||
struct ethtool_eeprom *eeprom, u8 *bytes)
|
|
||||||
{
|
|
||||||
struct atl1c_adapter *adapter = netdev_priv(netdev);
|
|
||||||
struct atl1c_hw *hw = &adapter->hw;
|
|
||||||
u32 *eeprom_buff;
|
|
||||||
int first_dword, last_dword;
|
|
||||||
int ret_val = 0;
|
|
||||||
int i;
|
|
||||||
|
|
||||||
if (eeprom->len == 0)
|
|
||||||
return -EINVAL;
|
|
||||||
|
|
||||||
if (!atl1c_check_eeprom_exist(hw)) /* not exist */
|
|
||||||
return -EINVAL;
|
|
||||||
|
|
||||||
eeprom->magic = adapter->pdev->vendor |
|
|
||||||
(adapter->pdev->device << 16);
|
|
||||||
|
|
||||||
first_dword = eeprom->offset >> 2;
|
|
||||||
last_dword = (eeprom->offset + eeprom->len - 1) >> 2;
|
|
||||||
|
|
||||||
eeprom_buff = kmalloc(sizeof(u32) *
|
|
||||||
(last_dword - first_dword + 1), GFP_KERNEL);
|
|
||||||
if (eeprom_buff == NULL)
|
|
||||||
return -ENOMEM;
|
|
||||||
|
|
||||||
for (i = first_dword; i < last_dword; i++) {
|
|
||||||
if (!atl1c_read_eeprom(hw, i * 4, &(eeprom_buff[i-first_dword]))) {
|
|
||||||
kfree(eeprom_buff);
|
|
||||||
return -EIO;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
memcpy(bytes, (u8 *)eeprom_buff + (eeprom->offset & 3),
|
|
||||||
eeprom->len);
|
|
||||||
kfree(eeprom_buff);
|
|
||||||
|
|
||||||
return ret_val;
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
static void atl1c_get_drvinfo(struct net_device *netdev,
|
|
||||||
struct ethtool_drvinfo *drvinfo)
|
|
||||||
{
|
|
||||||
struct atl1c_adapter *adapter = netdev_priv(netdev);
|
|
||||||
|
|
||||||
strlcpy(drvinfo->driver, atl1c_driver_name, sizeof(drvinfo->driver));
|
|
||||||
strlcpy(drvinfo->version, atl1c_driver_version,
|
|
||||||
sizeof(drvinfo->version));
|
|
||||||
strlcpy(drvinfo->bus_info, pci_name(adapter->pdev),
|
|
||||||
sizeof(drvinfo->bus_info));
|
|
||||||
drvinfo->n_stats = 0;
|
|
||||||
drvinfo->testinfo_len = 0;
|
|
||||||
drvinfo->regdump_len = atl1c_get_regs_len(netdev);
|
|
||||||
drvinfo->eedump_len = atl1c_get_eeprom_len(netdev);
|
|
||||||
}
|
|
||||||
|
|
||||||
static void atl1c_get_wol(struct net_device *netdev,
|
|
||||||
struct ethtool_wolinfo *wol)
|
|
||||||
{
|
|
||||||
struct atl1c_adapter *adapter = netdev_priv(netdev);
|
|
||||||
|
|
||||||
wol->supported = WAKE_MAGIC | WAKE_PHY;
|
|
||||||
wol->wolopts = 0;
|
|
||||||
|
|
||||||
if (adapter->wol & AT_WUFC_EX)
|
|
||||||
wol->wolopts |= WAKE_UCAST;
|
|
||||||
if (adapter->wol & AT_WUFC_MC)
|
|
||||||
wol->wolopts |= WAKE_MCAST;
|
|
||||||
if (adapter->wol & AT_WUFC_BC)
|
|
||||||
wol->wolopts |= WAKE_BCAST;
|
|
||||||
if (adapter->wol & AT_WUFC_MAG)
|
|
||||||
wol->wolopts |= WAKE_MAGIC;
|
|
||||||
if (adapter->wol & AT_WUFC_LNKC)
|
|
||||||
wol->wolopts |= WAKE_PHY;
|
|
||||||
}
|
|
||||||
|
|
||||||
static int atl1c_set_wol(struct net_device *netdev, struct ethtool_wolinfo *wol)
|
|
||||||
{
|
|
||||||
struct atl1c_adapter *adapter = netdev_priv(netdev);
|
|
||||||
|
|
||||||
if (wol->wolopts & (WAKE_ARP | WAKE_MAGICSECURE |
|
|
||||||
WAKE_UCAST | WAKE_BCAST | WAKE_MCAST))
|
|
||||||
return -EOPNOTSUPP;
|
|
||||||
/* these settings will always override what we currently have */
|
|
||||||
adapter->wol = 0;
|
|
||||||
|
|
||||||
if (wol->wolopts & WAKE_MAGIC)
|
|
||||||
adapter->wol |= AT_WUFC_MAG;
|
|
||||||
if (wol->wolopts & WAKE_PHY)
|
|
||||||
adapter->wol |= AT_WUFC_LNKC;
|
|
||||||
|
|
||||||
device_set_wakeup_enable(&adapter->pdev->dev, adapter->wol);
|
|
||||||
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
static int atl1c_nway_reset(struct net_device *netdev)
|
|
||||||
{
|
|
||||||
struct atl1c_adapter *adapter = netdev_priv(netdev);
|
|
||||||
if (netif_running(netdev))
|
|
||||||
atl1c_reinit_locked(adapter);
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
static const struct ethtool_ops atl1c_ethtool_ops = {
|
|
||||||
.get_settings = atl1c_get_settings,
|
|
||||||
.set_settings = atl1c_set_settings,
|
|
||||||
.get_drvinfo = atl1c_get_drvinfo,
|
|
||||||
.get_regs_len = atl1c_get_regs_len,
|
|
||||||
.get_regs = atl1c_get_regs,
|
|
||||||
.get_wol = atl1c_get_wol,
|
|
||||||
.set_wol = atl1c_set_wol,
|
|
||||||
.get_msglevel = atl1c_get_msglevel,
|
|
||||||
.set_msglevel = atl1c_set_msglevel,
|
|
||||||
.nway_reset = atl1c_nway_reset,
|
|
||||||
.get_link = ethtool_op_get_link,
|
|
||||||
.get_eeprom_len = atl1c_get_eeprom_len,
|
|
||||||
.get_eeprom = atl1c_get_eeprom,
|
|
||||||
};
|
|
||||||
|
|
||||||
void atl1c_set_ethtool_ops(struct net_device *netdev)
|
|
||||||
{
|
|
||||||
SET_ETHTOOL_OPS(netdev, &atl1c_ethtool_ops);
|
|
||||||
}
|
|
@ -1,865 +0,0 @@
|
|||||||
/*
|
|
||||||
* Copyright(c) 2007 Atheros Corporation. All rights reserved.
|
|
||||||
*
|
|
||||||
* Derived from Intel e1000 driver
|
|
||||||
* Copyright(c) 1999 - 2005 Intel Corporation. All rights reserved.
|
|
||||||
*
|
|
||||||
* This program is free software; you can redistribute it and/or modify it
|
|
||||||
* under the terms of the GNU General Public License as published by the Free
|
|
||||||
* Software Foundation; either version 2 of the License, or (at your option)
|
|
||||||
* any later version.
|
|
||||||
*
|
|
||||||
* This program is distributed in the hope that it will be useful, but WITHOUT
|
|
||||||
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
|
|
||||||
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
|
|
||||||
* more details.
|
|
||||||
*
|
|
||||||
* You should have received a copy of the GNU General Public License along with
|
|
||||||
* this program; if not, write to the Free Software Foundation, Inc., 59
|
|
||||||
* Temple Place - Suite 330, Boston, MA 02111-1307, USA.
|
|
||||||
*/
|
|
||||||
#include <linux/pci.h>
|
|
||||||
#include <linux/delay.h>
|
|
||||||
#include <linux/mii.h>
|
|
||||||
#include <linux/crc32.h>
|
|
||||||
|
|
||||||
#include "atl1c.h"
|
|
||||||
|
|
||||||
/*
|
|
||||||
* check_eeprom_exist
|
|
||||||
* return 1 if eeprom exist
|
|
||||||
*/
|
|
||||||
int atl1c_check_eeprom_exist(struct atl1c_hw *hw)
|
|
||||||
{
|
|
||||||
u32 data;
|
|
||||||
|
|
||||||
AT_READ_REG(hw, REG_TWSI_DEBUG, &data);
|
|
||||||
if (data & TWSI_DEBUG_DEV_EXIST)
|
|
||||||
return 1;
|
|
||||||
|
|
||||||
AT_READ_REG(hw, REG_MASTER_CTRL, &data);
|
|
||||||
if (data & MASTER_CTRL_OTP_SEL)
|
|
||||||
return 1;
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
void atl1c_hw_set_mac_addr(struct atl1c_hw *hw, u8 *mac_addr)
|
|
||||||
{
|
|
||||||
u32 value;
|
|
||||||
/*
|
|
||||||
* 00-0B-6A-F6-00-DC
|
|
||||||
* 0: 6AF600DC 1: 000B
|
|
||||||
* low dword
|
|
||||||
*/
|
|
||||||
value = mac_addr[2] << 24 |
|
|
||||||
mac_addr[3] << 16 |
|
|
||||||
mac_addr[4] << 8 |
|
|
||||||
mac_addr[5];
|
|
||||||
AT_WRITE_REG_ARRAY(hw, REG_MAC_STA_ADDR, 0, value);
|
|
||||||
/* hight dword */
|
|
||||||
value = mac_addr[0] << 8 |
|
|
||||||
mac_addr[1];
|
|
||||||
AT_WRITE_REG_ARRAY(hw, REG_MAC_STA_ADDR, 1, value);
|
|
||||||
}
|
|
||||||
|
|
||||||
/* read mac address from hardware register */
|
|
||||||
static bool atl1c_read_current_addr(struct atl1c_hw *hw, u8 *eth_addr)
|
|
||||||
{
|
|
||||||
u32 addr[2];
|
|
||||||
|
|
||||||
AT_READ_REG(hw, REG_MAC_STA_ADDR, &addr[0]);
|
|
||||||
AT_READ_REG(hw, REG_MAC_STA_ADDR + 4, &addr[1]);
|
|
||||||
|
|
||||||
*(u32 *) ð_addr[2] = htonl(addr[0]);
|
|
||||||
*(u16 *) ð_addr[0] = htons((u16)addr[1]);
|
|
||||||
|
|
||||||
return is_valid_ether_addr(eth_addr);
|
|
||||||
}
|
|
||||||
|
|
||||||
/*
|
|
||||||
* atl1c_get_permanent_address
|
|
||||||
* return 0 if get valid mac address,
|
|
||||||
*/
|
|
||||||
static int atl1c_get_permanent_address(struct atl1c_hw *hw)
|
|
||||||
{
|
|
||||||
u32 i;
|
|
||||||
u32 otp_ctrl_data;
|
|
||||||
u32 twsi_ctrl_data;
|
|
||||||
u16 phy_data;
|
|
||||||
bool raise_vol = false;
|
|
||||||
|
|
||||||
/* MAC-address from BIOS is the 1st priority */
|
|
||||||
if (atl1c_read_current_addr(hw, hw->perm_mac_addr))
|
|
||||||
return 0;
|
|
||||||
|
|
||||||
/* init */
|
|
||||||
AT_READ_REG(hw, REG_OTP_CTRL, &otp_ctrl_data);
|
|
||||||
if (atl1c_check_eeprom_exist(hw)) {
|
|
||||||
if (hw->nic_type == athr_l1c || hw->nic_type == athr_l2c) {
|
|
||||||
/* Enable OTP CLK */
|
|
||||||
if (!(otp_ctrl_data & OTP_CTRL_CLK_EN)) {
|
|
||||||
otp_ctrl_data |= OTP_CTRL_CLK_EN;
|
|
||||||
AT_WRITE_REG(hw, REG_OTP_CTRL, otp_ctrl_data);
|
|
||||||
AT_WRITE_FLUSH(hw);
|
|
||||||
msleep(1);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
/* raise voltage temporally for l2cb */
|
|
||||||
if (hw->nic_type == athr_l2c_b || hw->nic_type == athr_l2c_b2) {
|
|
||||||
atl1c_read_phy_dbg(hw, MIIDBG_ANACTRL, &phy_data);
|
|
||||||
phy_data &= ~ANACTRL_HB_EN;
|
|
||||||
atl1c_write_phy_dbg(hw, MIIDBG_ANACTRL, phy_data);
|
|
||||||
atl1c_read_phy_dbg(hw, MIIDBG_VOLT_CTRL, &phy_data);
|
|
||||||
phy_data |= VOLT_CTRL_SWLOWEST;
|
|
||||||
atl1c_write_phy_dbg(hw, MIIDBG_VOLT_CTRL, phy_data);
|
|
||||||
udelay(20);
|
|
||||||
raise_vol = true;
|
|
||||||
}
|
|
||||||
|
|
||||||
AT_READ_REG(hw, REG_TWSI_CTRL, &twsi_ctrl_data);
|
|
||||||
twsi_ctrl_data |= TWSI_CTRL_SW_LDSTART;
|
|
||||||
AT_WRITE_REG(hw, REG_TWSI_CTRL, twsi_ctrl_data);
|
|
||||||
for (i = 0; i < AT_TWSI_EEPROM_TIMEOUT; i++) {
|
|
||||||
msleep(10);
|
|
||||||
AT_READ_REG(hw, REG_TWSI_CTRL, &twsi_ctrl_data);
|
|
||||||
if ((twsi_ctrl_data & TWSI_CTRL_SW_LDSTART) == 0)
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
if (i >= AT_TWSI_EEPROM_TIMEOUT)
|
|
||||||
return -1;
|
|
||||||
}
|
|
||||||
/* Disable OTP_CLK */
|
|
||||||
if ((hw->nic_type == athr_l1c || hw->nic_type == athr_l2c)) {
|
|
||||||
otp_ctrl_data &= ~OTP_CTRL_CLK_EN;
|
|
||||||
AT_WRITE_REG(hw, REG_OTP_CTRL, otp_ctrl_data);
|
|
||||||
msleep(1);
|
|
||||||
}
|
|
||||||
if (raise_vol) {
|
|
||||||
atl1c_read_phy_dbg(hw, MIIDBG_ANACTRL, &phy_data);
|
|
||||||
phy_data |= ANACTRL_HB_EN;
|
|
||||||
atl1c_write_phy_dbg(hw, MIIDBG_ANACTRL, phy_data);
|
|
||||||
atl1c_read_phy_dbg(hw, MIIDBG_VOLT_CTRL, &phy_data);
|
|
||||||
phy_data &= ~VOLT_CTRL_SWLOWEST;
|
|
||||||
atl1c_write_phy_dbg(hw, MIIDBG_VOLT_CTRL, phy_data);
|
|
||||||
udelay(20);
|
|
||||||
}
|
|
||||||
|
|
||||||
if (atl1c_read_current_addr(hw, hw->perm_mac_addr))
|
|
||||||
return 0;
|
|
||||||
|
|
||||||
return -1;
|
|
||||||
}
|
|
||||||
|
|
||||||
bool atl1c_read_eeprom(struct atl1c_hw *hw, u32 offset, u32 *p_value)
|
|
||||||
{
|
|
||||||
int i;
|
|
||||||
int ret = false;
|
|
||||||
u32 otp_ctrl_data;
|
|
||||||
u32 control;
|
|
||||||
u32 data;
|
|
||||||
|
|
||||||
if (offset & 3)
|
|
||||||
return ret; /* address do not align */
|
|
||||||
|
|
||||||
AT_READ_REG(hw, REG_OTP_CTRL, &otp_ctrl_data);
|
|
||||||
if (!(otp_ctrl_data & OTP_CTRL_CLK_EN))
|
|
||||||
AT_WRITE_REG(hw, REG_OTP_CTRL,
|
|
||||||
(otp_ctrl_data | OTP_CTRL_CLK_EN));
|
|
||||||
|
|
||||||
AT_WRITE_REG(hw, REG_EEPROM_DATA_LO, 0);
|
|
||||||
control = (offset & EEPROM_CTRL_ADDR_MASK) << EEPROM_CTRL_ADDR_SHIFT;
|
|
||||||
AT_WRITE_REG(hw, REG_EEPROM_CTRL, control);
|
|
||||||
|
|
||||||
for (i = 0; i < 10; i++) {
|
|
||||||
udelay(100);
|
|
||||||
AT_READ_REG(hw, REG_EEPROM_CTRL, &control);
|
|
||||||
if (control & EEPROM_CTRL_RW)
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
if (control & EEPROM_CTRL_RW) {
|
|
||||||
AT_READ_REG(hw, REG_EEPROM_CTRL, &data);
|
|
||||||
AT_READ_REG(hw, REG_EEPROM_DATA_LO, p_value);
|
|
||||||
data = data & 0xFFFF;
|
|
||||||
*p_value = swab32((data << 16) | (*p_value >> 16));
|
|
||||||
ret = true;
|
|
||||||
}
|
|
||||||
if (!(otp_ctrl_data & OTP_CTRL_CLK_EN))
|
|
||||||
AT_WRITE_REG(hw, REG_OTP_CTRL, otp_ctrl_data);
|
|
||||||
|
|
||||||
return ret;
|
|
||||||
}
|
|
||||||
/*
|
|
||||||
* Reads the adapter's MAC address from the EEPROM
|
|
||||||
*
|
|
||||||
* hw - Struct containing variables accessed by shared code
|
|
||||||
*/
|
|
||||||
int atl1c_read_mac_addr(struct atl1c_hw *hw)
|
|
||||||
{
|
|
||||||
int err = 0;
|
|
||||||
|
|
||||||
err = atl1c_get_permanent_address(hw);
|
|
||||||
if (err)
|
|
||||||
eth_random_addr(hw->perm_mac_addr);
|
|
||||||
|
|
||||||
memcpy(hw->mac_addr, hw->perm_mac_addr, sizeof(hw->perm_mac_addr));
|
|
||||||
return err;
|
|
||||||
}
|
|
||||||
|
|
||||||
/*
|
|
||||||
* atl1c_hash_mc_addr
|
|
||||||
* purpose
|
|
||||||
* set hash value for a multicast address
|
|
||||||
* hash calcu processing :
|
|
||||||
* 1. calcu 32bit CRC for multicast address
|
|
||||||
* 2. reverse crc with MSB to LSB
|
|
||||||
*/
|
|
||||||
u32 atl1c_hash_mc_addr(struct atl1c_hw *hw, u8 *mc_addr)
|
|
||||||
{
|
|
||||||
u32 crc32;
|
|
||||||
u32 value = 0;
|
|
||||||
int i;
|
|
||||||
|
|
||||||
crc32 = ether_crc_le(6, mc_addr);
|
|
||||||
for (i = 0; i < 32; i++)
|
|
||||||
value |= (((crc32 >> i) & 1) << (31 - i));
|
|
||||||
|
|
||||||
return value;
|
|
||||||
}
|
|
||||||
|
|
||||||
/*
|
|
||||||
* Sets the bit in the multicast table corresponding to the hash value.
|
|
||||||
* hw - Struct containing variables accessed by shared code
|
|
||||||
* hash_value - Multicast address hash value
|
|
||||||
*/
|
|
||||||
void atl1c_hash_set(struct atl1c_hw *hw, u32 hash_value)
|
|
||||||
{
|
|
||||||
u32 hash_bit, hash_reg;
|
|
||||||
u32 mta;
|
|
||||||
|
|
||||||
/*
|
|
||||||
* The HASH Table is a register array of 2 32-bit registers.
|
|
||||||
* It is treated like an array of 64 bits. We want to set
|
|
||||||
* bit BitArray[hash_value]. So we figure out what register
|
|
||||||
* the bit is in, read it, OR in the new bit, then write
|
|
||||||
* back the new value. The register is determined by the
|
|
||||||
* upper bit of the hash value and the bit within that
|
|
||||||
* register are determined by the lower 5 bits of the value.
|
|
||||||
*/
|
|
||||||
hash_reg = (hash_value >> 31) & 0x1;
|
|
||||||
hash_bit = (hash_value >> 26) & 0x1F;
|
|
||||||
|
|
||||||
mta = AT_READ_REG_ARRAY(hw, REG_RX_HASH_TABLE, hash_reg);
|
|
||||||
|
|
||||||
mta |= (1 << hash_bit);
|
|
||||||
|
|
||||||
AT_WRITE_REG_ARRAY(hw, REG_RX_HASH_TABLE, hash_reg, mta);
|
|
||||||
}
|
|
||||||
|
|
||||||
/*
|
|
||||||
* wait mdio module be idle
|
|
||||||
* return true: idle
|
|
||||||
* false: still busy
|
|
||||||
*/
|
|
||||||
bool atl1c_wait_mdio_idle(struct atl1c_hw *hw)
|
|
||||||
{
|
|
||||||
u32 val;
|
|
||||||
int i;
|
|
||||||
|
|
||||||
for (i = 0; i < MDIO_MAX_AC_TO; i++) {
|
|
||||||
AT_READ_REG(hw, REG_MDIO_CTRL, &val);
|
|
||||||
if (!(val & (MDIO_CTRL_BUSY | MDIO_CTRL_START)))
|
|
||||||
break;
|
|
||||||
udelay(10);
|
|
||||||
}
|
|
||||||
|
|
||||||
return i != MDIO_MAX_AC_TO;
|
|
||||||
}
|
|
||||||
|
|
||||||
void atl1c_stop_phy_polling(struct atl1c_hw *hw)
|
|
||||||
{
|
|
||||||
if (!(hw->ctrl_flags & ATL1C_FPGA_VERSION))
|
|
||||||
return;
|
|
||||||
|
|
||||||
AT_WRITE_REG(hw, REG_MDIO_CTRL, 0);
|
|
||||||
atl1c_wait_mdio_idle(hw);
|
|
||||||
}
|
|
||||||
|
|
||||||
void atl1c_start_phy_polling(struct atl1c_hw *hw, u16 clk_sel)
|
|
||||||
{
|
|
||||||
u32 val;
|
|
||||||
|
|
||||||
if (!(hw->ctrl_flags & ATL1C_FPGA_VERSION))
|
|
||||||
return;
|
|
||||||
|
|
||||||
val = MDIO_CTRL_SPRES_PRMBL |
|
|
||||||
FIELDX(MDIO_CTRL_CLK_SEL, clk_sel) |
|
|
||||||
FIELDX(MDIO_CTRL_REG, 1) |
|
|
||||||
MDIO_CTRL_START |
|
|
||||||
MDIO_CTRL_OP_READ;
|
|
||||||
AT_WRITE_REG(hw, REG_MDIO_CTRL, val);
|
|
||||||
atl1c_wait_mdio_idle(hw);
|
|
||||||
val |= MDIO_CTRL_AP_EN;
|
|
||||||
val &= ~MDIO_CTRL_START;
|
|
||||||
AT_WRITE_REG(hw, REG_MDIO_CTRL, val);
|
|
||||||
udelay(30);
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
/*
|
|
||||||
* atl1c_read_phy_core
|
|
||||||
* core funtion to read register in PHY via MDIO control regsiter.
|
|
||||||
* ext: extension register (see IEEE 802.3)
|
|
||||||
* dev: device address (see IEEE 802.3 DEVAD, PRTAD is fixed to 0)
|
|
||||||
* reg: reg to read
|
|
||||||
*/
|
|
||||||
int atl1c_read_phy_core(struct atl1c_hw *hw, bool ext, u8 dev,
|
|
||||||
u16 reg, u16 *phy_data)
|
|
||||||
{
|
|
||||||
u32 val;
|
|
||||||
u16 clk_sel = MDIO_CTRL_CLK_25_4;
|
|
||||||
|
|
||||||
atl1c_stop_phy_polling(hw);
|
|
||||||
|
|
||||||
*phy_data = 0;
|
|
||||||
|
|
||||||
/* only l2c_b2 & l1d_2 could use slow clock */
|
|
||||||
if ((hw->nic_type == athr_l2c_b2 || hw->nic_type == athr_l1d_2) &&
|
|
||||||
hw->hibernate)
|
|
||||||
clk_sel = MDIO_CTRL_CLK_25_128;
|
|
||||||
if (ext) {
|
|
||||||
val = FIELDX(MDIO_EXTN_DEVAD, dev) | FIELDX(MDIO_EXTN_REG, reg);
|
|
||||||
AT_WRITE_REG(hw, REG_MDIO_EXTN, val);
|
|
||||||
val = MDIO_CTRL_SPRES_PRMBL |
|
|
||||||
FIELDX(MDIO_CTRL_CLK_SEL, clk_sel) |
|
|
||||||
MDIO_CTRL_START |
|
|
||||||
MDIO_CTRL_MODE_EXT |
|
|
||||||
MDIO_CTRL_OP_READ;
|
|
||||||
} else {
|
|
||||||
val = MDIO_CTRL_SPRES_PRMBL |
|
|
||||||
FIELDX(MDIO_CTRL_CLK_SEL, clk_sel) |
|
|
||||||
FIELDX(MDIO_CTRL_REG, reg) |
|
|
||||||
MDIO_CTRL_START |
|
|
||||||
MDIO_CTRL_OP_READ;
|
|
||||||
}
|
|
||||||
AT_WRITE_REG(hw, REG_MDIO_CTRL, val);
|
|
||||||
|
|
||||||
if (!atl1c_wait_mdio_idle(hw))
|
|
||||||
return -1;
|
|
||||||
|
|
||||||
AT_READ_REG(hw, REG_MDIO_CTRL, &val);
|
|
||||||
*phy_data = (u16)FIELD_GETX(val, MDIO_CTRL_DATA);
|
|
||||||
|
|
||||||
atl1c_start_phy_polling(hw, clk_sel);
|
|
||||||
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
/*
|
|
||||||
* atl1c_write_phy_core
|
|
||||||
* core funtion to write to register in PHY via MDIO control regsiter.
|
|
||||||
* ext: extension register (see IEEE 802.3)
|
|
||||||
* dev: device address (see IEEE 802.3 DEVAD, PRTAD is fixed to 0)
|
|
||||||
* reg: reg to write
|
|
||||||
*/
|
|
||||||
int atl1c_write_phy_core(struct atl1c_hw *hw, bool ext, u8 dev,
|
|
||||||
u16 reg, u16 phy_data)
|
|
||||||
{
|
|
||||||
u32 val;
|
|
||||||
u16 clk_sel = MDIO_CTRL_CLK_25_4;
|
|
||||||
|
|
||||||
atl1c_stop_phy_polling(hw);
|
|
||||||
|
|
||||||
|
|
||||||
/* only l2c_b2 & l1d_2 could use slow clock */
|
|
||||||
if ((hw->nic_type == athr_l2c_b2 || hw->nic_type == athr_l1d_2) &&
|
|
||||||
hw->hibernate)
|
|
||||||
clk_sel = MDIO_CTRL_CLK_25_128;
|
|
||||||
|
|
||||||
if (ext) {
|
|
||||||
val = FIELDX(MDIO_EXTN_DEVAD, dev) | FIELDX(MDIO_EXTN_REG, reg);
|
|
||||||
AT_WRITE_REG(hw, REG_MDIO_EXTN, val);
|
|
||||||
val = MDIO_CTRL_SPRES_PRMBL |
|
|
||||||
FIELDX(MDIO_CTRL_CLK_SEL, clk_sel) |
|
|
||||||
FIELDX(MDIO_CTRL_DATA, phy_data) |
|
|
||||||
MDIO_CTRL_START |
|
|
||||||
MDIO_CTRL_MODE_EXT;
|
|
||||||
} else {
|
|
||||||
val = MDIO_CTRL_SPRES_PRMBL |
|
|
||||||
FIELDX(MDIO_CTRL_CLK_SEL, clk_sel) |
|
|
||||||
FIELDX(MDIO_CTRL_DATA, phy_data) |
|
|
||||||
FIELDX(MDIO_CTRL_REG, reg) |
|
|
||||||
MDIO_CTRL_START;
|
|
||||||
}
|
|
||||||
AT_WRITE_REG(hw, REG_MDIO_CTRL, val);
|
|
||||||
|
|
||||||
if (!atl1c_wait_mdio_idle(hw))
|
|
||||||
return -1;
|
|
||||||
|
|
||||||
atl1c_start_phy_polling(hw, clk_sel);
|
|
||||||
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
/*
|
|
||||||
* Reads the value from a PHY register
|
|
||||||
* hw - Struct containing variables accessed by shared code
|
|
||||||
* reg_addr - address of the PHY register to read
|
|
||||||
*/
|
|
||||||
int atl1c_read_phy_reg(struct atl1c_hw *hw, u16 reg_addr, u16 *phy_data)
|
|
||||||
{
|
|
||||||
return atl1c_read_phy_core(hw, false, 0, reg_addr, phy_data);
|
|
||||||
}
|
|
||||||
|
|
||||||
/*
|
|
||||||
* Writes a value to a PHY register
|
|
||||||
* hw - Struct containing variables accessed by shared code
|
|
||||||
* reg_addr - address of the PHY register to write
|
|
||||||
* data - data to write to the PHY
|
|
||||||
*/
|
|
||||||
int atl1c_write_phy_reg(struct atl1c_hw *hw, u32 reg_addr, u16 phy_data)
|
|
||||||
{
|
|
||||||
return atl1c_write_phy_core(hw, false, 0, reg_addr, phy_data);
|
|
||||||
}
|
|
||||||
|
|
||||||
/* read from PHY extension register */
|
|
||||||
int atl1c_read_phy_ext(struct atl1c_hw *hw, u8 dev_addr,
|
|
||||||
u16 reg_addr, u16 *phy_data)
|
|
||||||
{
|
|
||||||
return atl1c_read_phy_core(hw, true, dev_addr, reg_addr, phy_data);
|
|
||||||
}
|
|
||||||
|
|
||||||
/* write to PHY extension register */
|
|
||||||
int atl1c_write_phy_ext(struct atl1c_hw *hw, u8 dev_addr,
|
|
||||||
u16 reg_addr, u16 phy_data)
|
|
||||||
{
|
|
||||||
return atl1c_write_phy_core(hw, true, dev_addr, reg_addr, phy_data);
|
|
||||||
}
|
|
||||||
|
|
||||||
int atl1c_read_phy_dbg(struct atl1c_hw *hw, u16 reg_addr, u16 *phy_data)
|
|
||||||
{
|
|
||||||
int err;
|
|
||||||
|
|
||||||
err = atl1c_write_phy_reg(hw, MII_DBG_ADDR, reg_addr);
|
|
||||||
if (unlikely(err))
|
|
||||||
return err;
|
|
||||||
else
|
|
||||||
err = atl1c_read_phy_reg(hw, MII_DBG_DATA, phy_data);
|
|
||||||
|
|
||||||
return err;
|
|
||||||
}
|
|
||||||
|
|
||||||
int atl1c_write_phy_dbg(struct atl1c_hw *hw, u16 reg_addr, u16 phy_data)
|
|
||||||
{
|
|
||||||
int err;
|
|
||||||
|
|
||||||
err = atl1c_write_phy_reg(hw, MII_DBG_ADDR, reg_addr);
|
|
||||||
if (unlikely(err))
|
|
||||||
return err;
|
|
||||||
else
|
|
||||||
err = atl1c_write_phy_reg(hw, MII_DBG_DATA, phy_data);
|
|
||||||
|
|
||||||
return err;
|
|
||||||
}
|
|
||||||
|
|
||||||
/*
|
|
||||||
* Configures PHY autoneg and flow control advertisement settings
|
|
||||||
*
|
|
||||||
* hw - Struct containing variables accessed by shared code
|
|
||||||
*/
|
|
||||||
static int atl1c_phy_setup_adv(struct atl1c_hw *hw)
|
|
||||||
{
|
|
||||||
u16 mii_adv_data = ADVERTISE_DEFAULT_CAP & ~ADVERTISE_ALL;
|
|
||||||
u16 mii_giga_ctrl_data = GIGA_CR_1000T_DEFAULT_CAP &
|
|
||||||
~GIGA_CR_1000T_SPEED_MASK;
|
|
||||||
|
|
||||||
if (hw->autoneg_advertised & ADVERTISED_10baseT_Half)
|
|
||||||
mii_adv_data |= ADVERTISE_10HALF;
|
|
||||||
if (hw->autoneg_advertised & ADVERTISED_10baseT_Full)
|
|
||||||
mii_adv_data |= ADVERTISE_10FULL;
|
|
||||||
if (hw->autoneg_advertised & ADVERTISED_100baseT_Half)
|
|
||||||
mii_adv_data |= ADVERTISE_100HALF;
|
|
||||||
if (hw->autoneg_advertised & ADVERTISED_100baseT_Full)
|
|
||||||
mii_adv_data |= ADVERTISE_100FULL;
|
|
||||||
|
|
||||||
if (hw->autoneg_advertised & ADVERTISED_Autoneg)
|
|
||||||
mii_adv_data |= ADVERTISE_10HALF | ADVERTISE_10FULL |
|
|
||||||
ADVERTISE_100HALF | ADVERTISE_100FULL;
|
|
||||||
|
|
||||||
if (hw->link_cap_flags & ATL1C_LINK_CAP_1000M) {
|
|
||||||
if (hw->autoneg_advertised & ADVERTISED_1000baseT_Half)
|
|
||||||
mii_giga_ctrl_data |= ADVERTISE_1000HALF;
|
|
||||||
if (hw->autoneg_advertised & ADVERTISED_1000baseT_Full)
|
|
||||||
mii_giga_ctrl_data |= ADVERTISE_1000FULL;
|
|
||||||
if (hw->autoneg_advertised & ADVERTISED_Autoneg)
|
|
||||||
mii_giga_ctrl_data |= ADVERTISE_1000HALF |
|
|
||||||
ADVERTISE_1000FULL;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (atl1c_write_phy_reg(hw, MII_ADVERTISE, mii_adv_data) != 0 ||
|
|
||||||
atl1c_write_phy_reg(hw, MII_CTRL1000, mii_giga_ctrl_data) != 0)
|
|
||||||
return -1;
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
void atl1c_phy_disable(struct atl1c_hw *hw)
|
|
||||||
{
|
|
||||||
atl1c_power_saving(hw, 0);
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
int atl1c_phy_reset(struct atl1c_hw *hw)
|
|
||||||
{
|
|
||||||
struct atl1c_adapter *adapter = hw->adapter;
|
|
||||||
struct pci_dev *pdev = adapter->pdev;
|
|
||||||
u16 phy_data;
|
|
||||||
u32 phy_ctrl_data, lpi_ctrl;
|
|
||||||
int err;
|
|
||||||
|
|
||||||
/* reset PHY core */
|
|
||||||
AT_READ_REG(hw, REG_GPHY_CTRL, &phy_ctrl_data);
|
|
||||||
phy_ctrl_data &= ~(GPHY_CTRL_EXT_RESET | GPHY_CTRL_PHY_IDDQ |
|
|
||||||
GPHY_CTRL_GATE_25M_EN | GPHY_CTRL_PWDOWN_HW | GPHY_CTRL_CLS);
|
|
||||||
phy_ctrl_data |= GPHY_CTRL_SEL_ANA_RST;
|
|
||||||
if (!(hw->ctrl_flags & ATL1C_HIB_DISABLE))
|
|
||||||
phy_ctrl_data |= (GPHY_CTRL_HIB_EN | GPHY_CTRL_HIB_PULSE);
|
|
||||||
else
|
|
||||||
phy_ctrl_data &= ~(GPHY_CTRL_HIB_EN | GPHY_CTRL_HIB_PULSE);
|
|
||||||
AT_WRITE_REG(hw, REG_GPHY_CTRL, phy_ctrl_data);
|
|
||||||
AT_WRITE_FLUSH(hw);
|
|
||||||
udelay(10);
|
|
||||||
AT_WRITE_REG(hw, REG_GPHY_CTRL, phy_ctrl_data | GPHY_CTRL_EXT_RESET);
|
|
||||||
AT_WRITE_FLUSH(hw);
|
|
||||||
udelay(10 * GPHY_CTRL_EXT_RST_TO); /* delay 800us */
|
|
||||||
|
|
||||||
/* switch clock */
|
|
||||||
if (hw->nic_type == athr_l2c_b) {
|
|
||||||
atl1c_read_phy_dbg(hw, MIIDBG_CFGLPSPD, &phy_data);
|
|
||||||
atl1c_write_phy_dbg(hw, MIIDBG_CFGLPSPD,
|
|
||||||
phy_data & ~CFGLPSPD_RSTCNT_CLK125SW);
|
|
||||||
}
|
|
||||||
|
|
||||||
/* tx-half amplitude issue fix */
|
|
||||||
if (hw->nic_type == athr_l2c_b || hw->nic_type == athr_l2c_b2) {
|
|
||||||
atl1c_read_phy_dbg(hw, MIIDBG_CABLE1TH_DET, &phy_data);
|
|
||||||
phy_data |= CABLE1TH_DET_EN;
|
|
||||||
atl1c_write_phy_dbg(hw, MIIDBG_CABLE1TH_DET, phy_data);
|
|
||||||
}
|
|
||||||
|
|
||||||
/* clear bit3 of dbgport 3B to lower voltage */
|
|
||||||
if (!(hw->ctrl_flags & ATL1C_HIB_DISABLE)) {
|
|
||||||
if (hw->nic_type == athr_l2c_b || hw->nic_type == athr_l2c_b2) {
|
|
||||||
atl1c_read_phy_dbg(hw, MIIDBG_VOLT_CTRL, &phy_data);
|
|
||||||
phy_data &= ~VOLT_CTRL_SWLOWEST;
|
|
||||||
atl1c_write_phy_dbg(hw, MIIDBG_VOLT_CTRL, phy_data);
|
|
||||||
}
|
|
||||||
/* power saving config */
|
|
||||||
phy_data =
|
|
||||||
hw->nic_type == athr_l1d || hw->nic_type == athr_l1d_2 ?
|
|
||||||
L1D_LEGCYPS_DEF : L1C_LEGCYPS_DEF;
|
|
||||||
atl1c_write_phy_dbg(hw, MIIDBG_LEGCYPS, phy_data);
|
|
||||||
/* hib */
|
|
||||||
atl1c_write_phy_dbg(hw, MIIDBG_SYSMODCTRL,
|
|
||||||
SYSMODCTRL_IECHOADJ_DEF);
|
|
||||||
} else {
|
|
||||||
/* disable pws */
|
|
||||||
atl1c_read_phy_dbg(hw, MIIDBG_LEGCYPS, &phy_data);
|
|
||||||
atl1c_write_phy_dbg(hw, MIIDBG_LEGCYPS,
|
|
||||||
phy_data & ~LEGCYPS_EN);
|
|
||||||
/* disable hibernate */
|
|
||||||
atl1c_read_phy_dbg(hw, MIIDBG_HIBNEG, &phy_data);
|
|
||||||
atl1c_write_phy_dbg(hw, MIIDBG_HIBNEG,
|
|
||||||
phy_data & HIBNEG_PSHIB_EN);
|
|
||||||
}
|
|
||||||
/* disable AZ(EEE) by default */
|
|
||||||
if (hw->nic_type == athr_l1d || hw->nic_type == athr_l1d_2 ||
|
|
||||||
hw->nic_type == athr_l2c_b2) {
|
|
||||||
AT_READ_REG(hw, REG_LPI_CTRL, &lpi_ctrl);
|
|
||||||
AT_WRITE_REG(hw, REG_LPI_CTRL, lpi_ctrl & ~LPI_CTRL_EN);
|
|
||||||
atl1c_write_phy_ext(hw, MIIEXT_ANEG, MIIEXT_LOCAL_EEEADV, 0);
|
|
||||||
atl1c_write_phy_ext(hw, MIIEXT_PCS, MIIEXT_CLDCTRL3,
|
|
||||||
L2CB_CLDCTRL3);
|
|
||||||
}
|
|
||||||
|
|
||||||
/* other debug port to set */
|
|
||||||
atl1c_write_phy_dbg(hw, MIIDBG_ANACTRL, ANACTRL_DEF);
|
|
||||||
atl1c_write_phy_dbg(hw, MIIDBG_SRDSYSMOD, SRDSYSMOD_DEF);
|
|
||||||
atl1c_write_phy_dbg(hw, MIIDBG_TST10BTCFG, TST10BTCFG_DEF);
|
|
||||||
/* UNH-IOL test issue, set bit7 */
|
|
||||||
atl1c_write_phy_dbg(hw, MIIDBG_TST100BTCFG,
|
|
||||||
TST100BTCFG_DEF | TST100BTCFG_LITCH_EN);
|
|
||||||
|
|
||||||
/* set phy interrupt mask */
|
|
||||||
phy_data = IER_LINK_UP | IER_LINK_DOWN;
|
|
||||||
err = atl1c_write_phy_reg(hw, MII_IER, phy_data);
|
|
||||||
if (err) {
|
|
||||||
if (netif_msg_hw(adapter))
|
|
||||||
dev_err(&pdev->dev,
|
|
||||||
"Error enable PHY linkChange Interrupt\n");
|
|
||||||
return err;
|
|
||||||
}
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
int atl1c_phy_init(struct atl1c_hw *hw)
|
|
||||||
{
|
|
||||||
struct atl1c_adapter *adapter = hw->adapter;
|
|
||||||
struct pci_dev *pdev = adapter->pdev;
|
|
||||||
int ret_val;
|
|
||||||
u16 mii_bmcr_data = BMCR_RESET;
|
|
||||||
|
|
||||||
if ((atl1c_read_phy_reg(hw, MII_PHYSID1, &hw->phy_id1) != 0) ||
|
|
||||||
(atl1c_read_phy_reg(hw, MII_PHYSID2, &hw->phy_id2) != 0)) {
|
|
||||||
dev_err(&pdev->dev, "Error get phy ID\n");
|
|
||||||
return -1;
|
|
||||||
}
|
|
||||||
switch (hw->media_type) {
|
|
||||||
case MEDIA_TYPE_AUTO_SENSOR:
|
|
||||||
ret_val = atl1c_phy_setup_adv(hw);
|
|
||||||
if (ret_val) {
|
|
||||||
if (netif_msg_link(adapter))
|
|
||||||
dev_err(&pdev->dev,
|
|
||||||
"Error Setting up Auto-Negotiation\n");
|
|
||||||
return ret_val;
|
|
||||||
}
|
|
||||||
mii_bmcr_data |= BMCR_ANENABLE | BMCR_ANRESTART;
|
|
||||||
break;
|
|
||||||
case MEDIA_TYPE_100M_FULL:
|
|
||||||
mii_bmcr_data |= BMCR_SPEED100 | BMCR_FULLDPLX;
|
|
||||||
break;
|
|
||||||
case MEDIA_TYPE_100M_HALF:
|
|
||||||
mii_bmcr_data |= BMCR_SPEED100;
|
|
||||||
break;
|
|
||||||
case MEDIA_TYPE_10M_FULL:
|
|
||||||
mii_bmcr_data |= BMCR_FULLDPLX;
|
|
||||||
break;
|
|
||||||
case MEDIA_TYPE_10M_HALF:
|
|
||||||
break;
|
|
||||||
default:
|
|
||||||
if (netif_msg_link(adapter))
|
|
||||||
dev_err(&pdev->dev, "Wrong Media type %d\n",
|
|
||||||
hw->media_type);
|
|
||||||
return -1;
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
|
|
||||||
ret_val = atl1c_write_phy_reg(hw, MII_BMCR, mii_bmcr_data);
|
|
||||||
if (ret_val)
|
|
||||||
return ret_val;
|
|
||||||
hw->phy_configured = true;
|
|
||||||
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
/*
|
|
||||||
* Detects the current speed and duplex settings of the hardware.
|
|
||||||
*
|
|
||||||
* hw - Struct containing variables accessed by shared code
|
|
||||||
* speed - Speed of the connection
|
|
||||||
* duplex - Duplex setting of the connection
|
|
||||||
*/
|
|
||||||
int atl1c_get_speed_and_duplex(struct atl1c_hw *hw, u16 *speed, u16 *duplex)
|
|
||||||
{
|
|
||||||
int err;
|
|
||||||
u16 phy_data;
|
|
||||||
|
|
||||||
/* Read PHY Specific Status Register (17) */
|
|
||||||
err = atl1c_read_phy_reg(hw, MII_GIGA_PSSR, &phy_data);
|
|
||||||
if (err)
|
|
||||||
return err;
|
|
||||||
|
|
||||||
if (!(phy_data & GIGA_PSSR_SPD_DPLX_RESOLVED))
|
|
||||||
return -1;
|
|
||||||
|
|
||||||
switch (phy_data & GIGA_PSSR_SPEED) {
|
|
||||||
case GIGA_PSSR_1000MBS:
|
|
||||||
*speed = SPEED_1000;
|
|
||||||
break;
|
|
||||||
case GIGA_PSSR_100MBS:
|
|
||||||
*speed = SPEED_100;
|
|
||||||
break;
|
|
||||||
case GIGA_PSSR_10MBS:
|
|
||||||
*speed = SPEED_10;
|
|
||||||
break;
|
|
||||||
default:
|
|
||||||
return -1;
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (phy_data & GIGA_PSSR_DPLX)
|
|
||||||
*duplex = FULL_DUPLEX;
|
|
||||||
else
|
|
||||||
*duplex = HALF_DUPLEX;
|
|
||||||
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
/* select one link mode to get lower power consumption */
|
|
||||||
int atl1c_phy_to_ps_link(struct atl1c_hw *hw)
|
|
||||||
{
|
|
||||||
struct atl1c_adapter *adapter = hw->adapter;
|
|
||||||
struct pci_dev *pdev = adapter->pdev;
|
|
||||||
int ret = 0;
|
|
||||||
u16 autoneg_advertised = ADVERTISED_10baseT_Half;
|
|
||||||
u16 save_autoneg_advertised;
|
|
||||||
u16 phy_data;
|
|
||||||
u16 mii_lpa_data;
|
|
||||||
u16 speed = SPEED_0;
|
|
||||||
u16 duplex = FULL_DUPLEX;
|
|
||||||
int i;
|
|
||||||
|
|
||||||
atl1c_read_phy_reg(hw, MII_BMSR, &phy_data);
|
|
||||||
atl1c_read_phy_reg(hw, MII_BMSR, &phy_data);
|
|
||||||
if (phy_data & BMSR_LSTATUS) {
|
|
||||||
atl1c_read_phy_reg(hw, MII_LPA, &mii_lpa_data);
|
|
||||||
if (mii_lpa_data & LPA_10FULL)
|
|
||||||
autoneg_advertised = ADVERTISED_10baseT_Full;
|
|
||||||
else if (mii_lpa_data & LPA_10HALF)
|
|
||||||
autoneg_advertised = ADVERTISED_10baseT_Half;
|
|
||||||
else if (mii_lpa_data & LPA_100HALF)
|
|
||||||
autoneg_advertised = ADVERTISED_100baseT_Half;
|
|
||||||
else if (mii_lpa_data & LPA_100FULL)
|
|
||||||
autoneg_advertised = ADVERTISED_100baseT_Full;
|
|
||||||
|
|
||||||
save_autoneg_advertised = hw->autoneg_advertised;
|
|
||||||
hw->phy_configured = false;
|
|
||||||
hw->autoneg_advertised = autoneg_advertised;
|
|
||||||
if (atl1c_restart_autoneg(hw) != 0) {
|
|
||||||
dev_dbg(&pdev->dev, "phy autoneg failed\n");
|
|
||||||
ret = -1;
|
|
||||||
}
|
|
||||||
hw->autoneg_advertised = save_autoneg_advertised;
|
|
||||||
|
|
||||||
if (mii_lpa_data) {
|
|
||||||
for (i = 0; i < AT_SUSPEND_LINK_TIMEOUT; i++) {
|
|
||||||
mdelay(100);
|
|
||||||
atl1c_read_phy_reg(hw, MII_BMSR, &phy_data);
|
|
||||||
atl1c_read_phy_reg(hw, MII_BMSR, &phy_data);
|
|
||||||
if (phy_data & BMSR_LSTATUS) {
|
|
||||||
if (atl1c_get_speed_and_duplex(hw, &speed,
|
|
||||||
&duplex) != 0)
|
|
||||||
dev_dbg(&pdev->dev,
|
|
||||||
"get speed and duplex failed\n");
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
speed = SPEED_10;
|
|
||||||
duplex = HALF_DUPLEX;
|
|
||||||
}
|
|
||||||
adapter->link_speed = speed;
|
|
||||||
adapter->link_duplex = duplex;
|
|
||||||
|
|
||||||
return ret;
|
|
||||||
}
|
|
||||||
|
|
||||||
int atl1c_restart_autoneg(struct atl1c_hw *hw)
|
|
||||||
{
|
|
||||||
int err = 0;
|
|
||||||
u16 mii_bmcr_data = BMCR_RESET;
|
|
||||||
|
|
||||||
err = atl1c_phy_setup_adv(hw);
|
|
||||||
if (err)
|
|
||||||
return err;
|
|
||||||
mii_bmcr_data |= BMCR_ANENABLE | BMCR_ANRESTART;
|
|
||||||
|
|
||||||
return atl1c_write_phy_reg(hw, MII_BMCR, mii_bmcr_data);
|
|
||||||
}
|
|
||||||
|
|
||||||
int atl1c_power_saving(struct atl1c_hw *hw, u32 wufc)
|
|
||||||
{
|
|
||||||
struct atl1c_adapter *adapter = hw->adapter;
|
|
||||||
struct pci_dev *pdev = adapter->pdev;
|
|
||||||
u32 master_ctrl, mac_ctrl, phy_ctrl;
|
|
||||||
u32 wol_ctrl, speed;
|
|
||||||
u16 phy_data;
|
|
||||||
|
|
||||||
wol_ctrl = 0;
|
|
||||||
speed = adapter->link_speed == SPEED_1000 ?
|
|
||||||
MAC_CTRL_SPEED_1000 : MAC_CTRL_SPEED_10_100;
|
|
||||||
|
|
||||||
AT_READ_REG(hw, REG_MASTER_CTRL, &master_ctrl);
|
|
||||||
AT_READ_REG(hw, REG_MAC_CTRL, &mac_ctrl);
|
|
||||||
AT_READ_REG(hw, REG_GPHY_CTRL, &phy_ctrl);
|
|
||||||
|
|
||||||
master_ctrl &= ~MASTER_CTRL_CLK_SEL_DIS;
|
|
||||||
mac_ctrl = FIELD_SETX(mac_ctrl, MAC_CTRL_SPEED, speed);
|
|
||||||
mac_ctrl &= ~(MAC_CTRL_DUPLX | MAC_CTRL_RX_EN | MAC_CTRL_TX_EN);
|
|
||||||
if (adapter->link_duplex == FULL_DUPLEX)
|
|
||||||
mac_ctrl |= MAC_CTRL_DUPLX;
|
|
||||||
phy_ctrl &= ~(GPHY_CTRL_EXT_RESET | GPHY_CTRL_CLS);
|
|
||||||
phy_ctrl |= GPHY_CTRL_SEL_ANA_RST | GPHY_CTRL_HIB_PULSE |
|
|
||||||
GPHY_CTRL_HIB_EN;
|
|
||||||
if (!wufc) { /* without WoL */
|
|
||||||
master_ctrl |= MASTER_CTRL_CLK_SEL_DIS;
|
|
||||||
phy_ctrl |= GPHY_CTRL_PHY_IDDQ | GPHY_CTRL_PWDOWN_HW;
|
|
||||||
AT_WRITE_REG(hw, REG_MASTER_CTRL, master_ctrl);
|
|
||||||
AT_WRITE_REG(hw, REG_MAC_CTRL, mac_ctrl);
|
|
||||||
AT_WRITE_REG(hw, REG_GPHY_CTRL, phy_ctrl);
|
|
||||||
AT_WRITE_REG(hw, REG_WOL_CTRL, 0);
|
|
||||||
hw->phy_configured = false; /* re-init PHY when resume */
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
phy_ctrl |= GPHY_CTRL_EXT_RESET;
|
|
||||||
if (wufc & AT_WUFC_MAG) {
|
|
||||||
mac_ctrl |= MAC_CTRL_RX_EN | MAC_CTRL_BC_EN;
|
|
||||||
wol_ctrl |= WOL_MAGIC_EN | WOL_MAGIC_PME_EN;
|
|
||||||
if (hw->nic_type == athr_l2c_b && hw->revision_id == L2CB_V11)
|
|
||||||
wol_ctrl |= WOL_PATTERN_EN | WOL_PATTERN_PME_EN;
|
|
||||||
}
|
|
||||||
if (wufc & AT_WUFC_LNKC) {
|
|
||||||
wol_ctrl |= WOL_LINK_CHG_EN | WOL_LINK_CHG_PME_EN;
|
|
||||||
if (atl1c_write_phy_reg(hw, MII_IER, IER_LINK_UP) != 0) {
|
|
||||||
dev_dbg(&pdev->dev, "%s: write phy MII_IER failed.\n",
|
|
||||||
atl1c_driver_name);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
/* clear PHY interrupt */
|
|
||||||
atl1c_read_phy_reg(hw, MII_ISR, &phy_data);
|
|
||||||
|
|
||||||
dev_dbg(&pdev->dev, "%s: suspend MAC=%x,MASTER=%x,PHY=0x%x,WOL=%x\n",
|
|
||||||
atl1c_driver_name, mac_ctrl, master_ctrl, phy_ctrl, wol_ctrl);
|
|
||||||
AT_WRITE_REG(hw, REG_MASTER_CTRL, master_ctrl);
|
|
||||||
AT_WRITE_REG(hw, REG_MAC_CTRL, mac_ctrl);
|
|
||||||
AT_WRITE_REG(hw, REG_GPHY_CTRL, phy_ctrl);
|
|
||||||
AT_WRITE_REG(hw, REG_WOL_CTRL, wol_ctrl);
|
|
||||||
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
/* configure phy after Link change Event */
|
|
||||||
void atl1c_post_phy_linkchg(struct atl1c_hw *hw, u16 link_speed)
|
|
||||||
{
|
|
||||||
u16 phy_val;
|
|
||||||
bool adj_thresh = false;
|
|
||||||
|
|
||||||
if (hw->nic_type == athr_l2c_b || hw->nic_type == athr_l2c_b2 ||
|
|
||||||
hw->nic_type == athr_l1d || hw->nic_type == athr_l1d_2)
|
|
||||||
adj_thresh = true;
|
|
||||||
|
|
||||||
if (link_speed != SPEED_0) { /* link up */
|
|
||||||
/* az with brcm, half-amp */
|
|
||||||
if (hw->nic_type == athr_l1d_2) {
|
|
||||||
atl1c_read_phy_ext(hw, MIIEXT_PCS, MIIEXT_CLDCTRL6,
|
|
||||||
&phy_val);
|
|
||||||
phy_val = FIELD_GETX(phy_val, CLDCTRL6_CAB_LEN);
|
|
||||||
phy_val = phy_val > CLDCTRL6_CAB_LEN_SHORT ?
|
|
||||||
AZ_ANADECT_LONG : AZ_ANADECT_DEF;
|
|
||||||
atl1c_write_phy_dbg(hw, MIIDBG_AZ_ANADECT, phy_val);
|
|
||||||
}
|
|
||||||
/* threshold adjust */
|
|
||||||
if (adj_thresh && link_speed == SPEED_100 && hw->msi_lnkpatch) {
|
|
||||||
atl1c_write_phy_dbg(hw, MIIDBG_MSE16DB, L1D_MSE16DB_UP);
|
|
||||||
atl1c_write_phy_dbg(hw, MIIDBG_SYSMODCTRL,
|
|
||||||
L1D_SYSMODCTRL_IECHOADJ_DEF);
|
|
||||||
}
|
|
||||||
} else { /* link down */
|
|
||||||
if (adj_thresh && hw->msi_lnkpatch) {
|
|
||||||
atl1c_write_phy_dbg(hw, MIIDBG_SYSMODCTRL,
|
|
||||||
SYSMODCTRL_IECHOADJ_DEF);
|
|
||||||
atl1c_write_phy_dbg(hw, MIIDBG_MSE16DB,
|
|
||||||
L1D_MSE16DB_DOWN);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
@ -1,2 +0,0 @@
|
|||||||
obj-m += atl1c.o
|
|
||||||
atl1c-objs := atl1c_main.o atl1c_hw.o atl1c_ethtool.o
|
|
@ -1,605 +0,0 @@
|
|||||||
/*
|
|
||||||
* Copyright(c) 2008 - 2009 Atheros Corporation. All rights reserved.
|
|
||||||
*
|
|
||||||
* Derived from Intel e1000 driver
|
|
||||||
* Copyright(c) 1999 - 2005 Intel Corporation. All rights reserved.
|
|
||||||
*
|
|
||||||
* This program is free software; you can redistribute it and/or modify it
|
|
||||||
* under the terms of the GNU General Public License as published by the Free
|
|
||||||
* Software Foundation; either version 2 of the License, or (at your option)
|
|
||||||
* any later version.
|
|
||||||
*
|
|
||||||
* This program is distributed in the hope that it will be useful, but WITHOUT
|
|
||||||
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
|
|
||||||
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
|
|
||||||
* more details.
|
|
||||||
*
|
|
||||||
* You should have received a copy of the GNU General Public License along with
|
|
||||||
* this program; if not, write to the Free Software Foundation, Inc., 59
|
|
||||||
* Temple Place - Suite 330, Boston, MA 02111-1307, USA.
|
|
||||||
*/
|
|
||||||
|
|
||||||
#ifndef _ATL1C_H_
|
|
||||||
#define _ATL1C_H_
|
|
||||||
|
|
||||||
#include <linux/interrupt.h>
|
|
||||||
#include <linux/types.h>
|
|
||||||
#include <linux/errno.h>
|
|
||||||
#include <linux/module.h>
|
|
||||||
#include <linux/pci.h>
|
|
||||||
#include <linux/netdevice.h>
|
|
||||||
#include <linux/etherdevice.h>
|
|
||||||
#include <linux/skbuff.h>
|
|
||||||
#include <linux/ioport.h>
|
|
||||||
#include <linux/slab.h>
|
|
||||||
#include <linux/list.h>
|
|
||||||
#include <linux/delay.h>
|
|
||||||
#include <linux/sched.h>
|
|
||||||
#include <linux/in.h>
|
|
||||||
#include <linux/ip.h>
|
|
||||||
#include <linux/ipv6.h>
|
|
||||||
#include <linux/udp.h>
|
|
||||||
#include <linux/mii.h>
|
|
||||||
#include <linux/io.h>
|
|
||||||
#include <linux/vmalloc.h>
|
|
||||||
#include <linux/pagemap.h>
|
|
||||||
#include <linux/tcp.h>
|
|
||||||
#include <linux/ethtool.h>
|
|
||||||
#include <linux/if_vlan.h>
|
|
||||||
#include <linux/workqueue.h>
|
|
||||||
#include <net/checksum.h>
|
|
||||||
#include <net/ip6_checksum.h>
|
|
||||||
|
|
||||||
#include "atl1c_hw.h"
|
|
||||||
|
|
||||||
/* Wake Up Filter Control */
|
|
||||||
#define AT_WUFC_LNKC 0x00000001 /* Link Status Change Wakeup Enable */
|
|
||||||
#define AT_WUFC_MAG 0x00000002 /* Magic Packet Wakeup Enable */
|
|
||||||
#define AT_WUFC_EX 0x00000004 /* Directed Exact Wakeup Enable */
|
|
||||||
#define AT_WUFC_MC 0x00000008 /* Multicast Wakeup Enable */
|
|
||||||
#define AT_WUFC_BC 0x00000010 /* Broadcast Wakeup Enable */
|
|
||||||
|
|
||||||
#define AT_VLAN_TO_TAG(_vlan, _tag) \
|
|
||||||
_tag = ((((_vlan) >> 8) & 0xFF) |\
|
|
||||||
(((_vlan) & 0xFF) << 8))
|
|
||||||
|
|
||||||
#define AT_TAG_TO_VLAN(_tag, _vlan) \
|
|
||||||
_vlan = ((((_tag) >> 8) & 0xFF) |\
|
|
||||||
(((_tag) & 0xFF) << 8))
|
|
||||||
|
|
||||||
#define SPEED_0 0xffff
|
|
||||||
#define HALF_DUPLEX 1
|
|
||||||
#define FULL_DUPLEX 2
|
|
||||||
|
|
||||||
#define AT_RX_BUF_SIZE (ETH_FRAME_LEN + VLAN_HLEN + ETH_FCS_LEN)
|
|
||||||
#define MAX_JUMBO_FRAME_SIZE (6*1024)
|
|
||||||
|
|
||||||
#define AT_MAX_RECEIVE_QUEUE 4
|
|
||||||
#define AT_DEF_RECEIVE_QUEUE 1
|
|
||||||
#define AT_MAX_TRANSMIT_QUEUE 2
|
|
||||||
|
|
||||||
#define AT_DMA_HI_ADDR_MASK 0xffffffff00000000ULL
|
|
||||||
#define AT_DMA_LO_ADDR_MASK 0x00000000ffffffffULL
|
|
||||||
|
|
||||||
#define AT_TX_WATCHDOG (5 * HZ)
|
|
||||||
#define AT_MAX_INT_WORK 5
|
|
||||||
#define AT_TWSI_EEPROM_TIMEOUT 100
|
|
||||||
#define AT_HW_MAX_IDLE_DELAY 10
|
|
||||||
#define AT_SUSPEND_LINK_TIMEOUT 100
|
|
||||||
|
|
||||||
#define AT_ASPM_L0S_TIMER 6
|
|
||||||
#define AT_ASPM_L1_TIMER 12
|
|
||||||
#define AT_LCKDET_TIMER 12
|
|
||||||
|
|
||||||
#define ATL1C_PCIE_L0S_L1_DISABLE 0x01
|
|
||||||
#define ATL1C_PCIE_PHY_RESET 0x02
|
|
||||||
|
|
||||||
#define ATL1C_ASPM_L0s_ENABLE 0x0001
|
|
||||||
#define ATL1C_ASPM_L1_ENABLE 0x0002
|
|
||||||
|
|
||||||
#define AT_REGS_LEN (74 * sizeof(u32))
|
|
||||||
#define AT_EEPROM_LEN 512
|
|
||||||
|
|
||||||
#define ATL1C_GET_DESC(R, i, type) (&(((type *)((R)->desc))[i]))
|
|
||||||
#define ATL1C_RFD_DESC(R, i) ATL1C_GET_DESC(R, i, struct atl1c_rx_free_desc)
|
|
||||||
#define ATL1C_TPD_DESC(R, i) ATL1C_GET_DESC(R, i, struct atl1c_tpd_desc)
|
|
||||||
#define ATL1C_RRD_DESC(R, i) ATL1C_GET_DESC(R, i, struct atl1c_recv_ret_status)
|
|
||||||
|
|
||||||
/* tpd word 1 bit 0:7 General Checksum task offload */
|
|
||||||
#define TPD_L4HDR_OFFSET_MASK 0x00FF
|
|
||||||
#define TPD_L4HDR_OFFSET_SHIFT 0
|
|
||||||
|
|
||||||
/* tpd word 1 bit 0:7 Large Send task offload (IPv4/IPV6) */
|
|
||||||
#define TPD_TCPHDR_OFFSET_MASK 0x00FF
|
|
||||||
#define TPD_TCPHDR_OFFSET_SHIFT 0
|
|
||||||
|
|
||||||
/* tpd word 1 bit 0:7 Custom Checksum task offload */
|
|
||||||
#define TPD_PLOADOFFSET_MASK 0x00FF
|
|
||||||
#define TPD_PLOADOFFSET_SHIFT 0
|
|
||||||
|
|
||||||
/* tpd word 1 bit 8:17 */
|
|
||||||
#define TPD_CCSUM_EN_MASK 0x0001
|
|
||||||
#define TPD_CCSUM_EN_SHIFT 8
|
|
||||||
#define TPD_IP_CSUM_MASK 0x0001
|
|
||||||
#define TPD_IP_CSUM_SHIFT 9
|
|
||||||
#define TPD_TCP_CSUM_MASK 0x0001
|
|
||||||
#define TPD_TCP_CSUM_SHIFT 10
|
|
||||||
#define TPD_UDP_CSUM_MASK 0x0001
|
|
||||||
#define TPD_UDP_CSUM_SHIFT 11
|
|
||||||
#define TPD_LSO_EN_MASK 0x0001 /* TCP Large Send Offload */
|
|
||||||
#define TPD_LSO_EN_SHIFT 12
|
|
||||||
#define TPD_LSO_VER_MASK 0x0001
|
|
||||||
#define TPD_LSO_VER_SHIFT 13 /* 0 : ipv4; 1 : ipv4/ipv6 */
|
|
||||||
#define TPD_CON_VTAG_MASK 0x0001
|
|
||||||
#define TPD_CON_VTAG_SHIFT 14
|
|
||||||
#define TPD_INS_VTAG_MASK 0x0001
|
|
||||||
#define TPD_INS_VTAG_SHIFT 15
|
|
||||||
#define TPD_IPV4_PACKET_MASK 0x0001 /* valid when LSO VER is 1 */
|
|
||||||
#define TPD_IPV4_PACKET_SHIFT 16
|
|
||||||
#define TPD_ETH_TYPE_MASK 0x0001
|
|
||||||
#define TPD_ETH_TYPE_SHIFT 17 /* 0 : 802.3 frame; 1 : Ethernet */
|
|
||||||
|
|
||||||
/* tpd word 18:25 Custom Checksum task offload */
|
|
||||||
#define TPD_CCSUM_OFFSET_MASK 0x00FF
|
|
||||||
#define TPD_CCSUM_OFFSET_SHIFT 18
|
|
||||||
#define TPD_CCSUM_EPAD_MASK 0x0001
|
|
||||||
#define TPD_CCSUM_EPAD_SHIFT 30
|
|
||||||
|
|
||||||
/* tpd word 18:30 Large Send task offload (IPv4/IPV6) */
|
|
||||||
#define TPD_MSS_MASK 0x1FFF
|
|
||||||
#define TPD_MSS_SHIFT 18
|
|
||||||
|
|
||||||
#define TPD_EOP_MASK 0x0001
|
|
||||||
#define TPD_EOP_SHIFT 31
|
|
||||||
|
|
||||||
struct atl1c_tpd_desc {
|
|
||||||
__le16 buffer_len; /* include 4-byte CRC */
|
|
||||||
__le16 vlan_tag;
|
|
||||||
__le32 word1;
|
|
||||||
__le64 buffer_addr;
|
|
||||||
};
|
|
||||||
|
|
||||||
struct atl1c_tpd_ext_desc {
|
|
||||||
u32 reservd_0;
|
|
||||||
__le32 word1;
|
|
||||||
__le32 pkt_len;
|
|
||||||
u32 reservd_1;
|
|
||||||
};
|
|
||||||
/* rrs word 0 bit 0:31 */
|
|
||||||
#define RRS_RX_CSUM_MASK 0xFFFF
|
|
||||||
#define RRS_RX_CSUM_SHIFT 0
|
|
||||||
#define RRS_RX_RFD_CNT_MASK 0x000F
|
|
||||||
#define RRS_RX_RFD_CNT_SHIFT 16
|
|
||||||
#define RRS_RX_RFD_INDEX_MASK 0x0FFF
|
|
||||||
#define RRS_RX_RFD_INDEX_SHIFT 20
|
|
||||||
|
|
||||||
/* rrs flag bit 0:16 */
|
|
||||||
#define RRS_HEAD_LEN_MASK 0x00FF
|
|
||||||
#define RRS_HEAD_LEN_SHIFT 0
|
|
||||||
#define RRS_HDS_TYPE_MASK 0x0003
|
|
||||||
#define RRS_HDS_TYPE_SHIFT 8
|
|
||||||
#define RRS_CPU_NUM_MASK 0x0003
|
|
||||||
#define RRS_CPU_NUM_SHIFT 10
|
|
||||||
#define RRS_HASH_FLG_MASK 0x000F
|
|
||||||
#define RRS_HASH_FLG_SHIFT 12
|
|
||||||
|
|
||||||
#define RRS_HDS_TYPE_HEAD 1
|
|
||||||
#define RRS_HDS_TYPE_DATA 2
|
|
||||||
|
|
||||||
#define RRS_IS_NO_HDS_TYPE(flag) \
|
|
||||||
((((flag) >> (RRS_HDS_TYPE_SHIFT)) & RRS_HDS_TYPE_MASK) == 0)
|
|
||||||
|
|
||||||
#define RRS_IS_HDS_HEAD(flag) \
|
|
||||||
((((flag) >> (RRS_HDS_TYPE_SHIFT)) & RRS_HDS_TYPE_MASK) == \
|
|
||||||
RRS_HDS_TYPE_HEAD)
|
|
||||||
|
|
||||||
#define RRS_IS_HDS_DATA(flag) \
|
|
||||||
((((flag) >> (RRS_HDS_TYPE_SHIFT)) & RRS_HDS_TYPE_MASK) == \
|
|
||||||
RRS_HDS_TYPE_DATA)
|
|
||||||
|
|
||||||
/* rrs word 3 bit 0:31 */
|
|
||||||
#define RRS_PKT_SIZE_MASK 0x3FFF
|
|
||||||
#define RRS_PKT_SIZE_SHIFT 0
|
|
||||||
#define RRS_ERR_L4_CSUM_MASK 0x0001
|
|
||||||
#define RRS_ERR_L4_CSUM_SHIFT 14
|
|
||||||
#define RRS_ERR_IP_CSUM_MASK 0x0001
|
|
||||||
#define RRS_ERR_IP_CSUM_SHIFT 15
|
|
||||||
#define RRS_VLAN_INS_MASK 0x0001
|
|
||||||
#define RRS_VLAN_INS_SHIFT 16
|
|
||||||
#define RRS_PROT_ID_MASK 0x0007
|
|
||||||
#define RRS_PROT_ID_SHIFT 17
|
|
||||||
#define RRS_RX_ERR_SUM_MASK 0x0001
|
|
||||||
#define RRS_RX_ERR_SUM_SHIFT 20
|
|
||||||
#define RRS_RX_ERR_CRC_MASK 0x0001
|
|
||||||
#define RRS_RX_ERR_CRC_SHIFT 21
|
|
||||||
#define RRS_RX_ERR_FAE_MASK 0x0001
|
|
||||||
#define RRS_RX_ERR_FAE_SHIFT 22
|
|
||||||
#define RRS_RX_ERR_TRUNC_MASK 0x0001
|
|
||||||
#define RRS_RX_ERR_TRUNC_SHIFT 23
|
|
||||||
#define RRS_RX_ERR_RUNC_MASK 0x0001
|
|
||||||
#define RRS_RX_ERR_RUNC_SHIFT 24
|
|
||||||
#define RRS_RX_ERR_ICMP_MASK 0x0001
|
|
||||||
#define RRS_RX_ERR_ICMP_SHIFT 25
|
|
||||||
#define RRS_PACKET_BCAST_MASK 0x0001
|
|
||||||
#define RRS_PACKET_BCAST_SHIFT 26
|
|
||||||
#define RRS_PACKET_MCAST_MASK 0x0001
|
|
||||||
#define RRS_PACKET_MCAST_SHIFT 27
|
|
||||||
#define RRS_PACKET_TYPE_MASK 0x0001
|
|
||||||
#define RRS_PACKET_TYPE_SHIFT 28
|
|
||||||
#define RRS_FIFO_FULL_MASK 0x0001
|
|
||||||
#define RRS_FIFO_FULL_SHIFT 29
|
|
||||||
#define RRS_802_3_LEN_ERR_MASK 0x0001
|
|
||||||
#define RRS_802_3_LEN_ERR_SHIFT 30
|
|
||||||
#define RRS_RXD_UPDATED_MASK 0x0001
|
|
||||||
#define RRS_RXD_UPDATED_SHIFT 31
|
|
||||||
|
|
||||||
#define RRS_ERR_L4_CSUM 0x00004000
|
|
||||||
#define RRS_ERR_IP_CSUM 0x00008000
|
|
||||||
#define RRS_VLAN_INS 0x00010000
|
|
||||||
#define RRS_RX_ERR_SUM 0x00100000
|
|
||||||
#define RRS_RX_ERR_CRC 0x00200000
|
|
||||||
#define RRS_802_3_LEN_ERR 0x40000000
|
|
||||||
#define RRS_RXD_UPDATED 0x80000000
|
|
||||||
|
|
||||||
#define RRS_PACKET_TYPE_802_3 1
|
|
||||||
#define RRS_PACKET_TYPE_ETH 0
|
|
||||||
#define RRS_PACKET_IS_ETH(word) \
|
|
||||||
((((word) >> RRS_PACKET_TYPE_SHIFT) & RRS_PACKET_TYPE_MASK) == \
|
|
||||||
RRS_PACKET_TYPE_ETH)
|
|
||||||
#define RRS_RXD_IS_VALID(word) \
|
|
||||||
((((word) >> RRS_RXD_UPDATED_SHIFT) & RRS_RXD_UPDATED_MASK) == 1)
|
|
||||||
|
|
||||||
#define RRS_PACKET_PROT_IS_IPV4_ONLY(word) \
|
|
||||||
((((word) >> RRS_PROT_ID_SHIFT) & RRS_PROT_ID_MASK) == 1)
|
|
||||||
#define RRS_PACKET_PROT_IS_IPV6_ONLY(word) \
|
|
||||||
((((word) >> RRS_PROT_ID_SHIFT) & RRS_PROT_ID_MASK) == 6)
|
|
||||||
|
|
||||||
struct atl1c_recv_ret_status {
|
|
||||||
__le32 word0;
|
|
||||||
__le32 rss_hash;
|
|
||||||
__le16 vlan_tag;
|
|
||||||
__le16 flag;
|
|
||||||
__le32 word3;
|
|
||||||
};
|
|
||||||
|
|
||||||
/* RFD descriptor */
|
|
||||||
struct atl1c_rx_free_desc {
|
|
||||||
__le64 buffer_addr;
|
|
||||||
};
|
|
||||||
|
|
||||||
/* DMA Order Settings */
|
|
||||||
enum atl1c_dma_order {
|
|
||||||
atl1c_dma_ord_in = 1,
|
|
||||||
atl1c_dma_ord_enh = 2,
|
|
||||||
atl1c_dma_ord_out = 4
|
|
||||||
};
|
|
||||||
|
|
||||||
enum atl1c_dma_rcb {
|
|
||||||
atl1c_rcb_64 = 0,
|
|
||||||
atl1c_rcb_128 = 1
|
|
||||||
};
|
|
||||||
|
|
||||||
enum atl1c_mac_speed {
|
|
||||||
atl1c_mac_speed_0 = 0,
|
|
||||||
atl1c_mac_speed_10_100 = 1,
|
|
||||||
atl1c_mac_speed_1000 = 2
|
|
||||||
};
|
|
||||||
|
|
||||||
enum atl1c_dma_req_block {
|
|
||||||
atl1c_dma_req_128 = 0,
|
|
||||||
atl1c_dma_req_256 = 1,
|
|
||||||
atl1c_dma_req_512 = 2,
|
|
||||||
atl1c_dma_req_1024 = 3,
|
|
||||||
atl1c_dma_req_2048 = 4,
|
|
||||||
atl1c_dma_req_4096 = 5
|
|
||||||
};
|
|
||||||
|
|
||||||
|
|
||||||
enum atl1c_nic_type {
|
|
||||||
athr_l1c = 0,
|
|
||||||
athr_l2c = 1,
|
|
||||||
athr_l2c_b,
|
|
||||||
athr_l2c_b2,
|
|
||||||
athr_l1d,
|
|
||||||
athr_l1d_2,
|
|
||||||
};
|
|
||||||
|
|
||||||
enum atl1c_trans_queue {
|
|
||||||
atl1c_trans_normal = 0,
|
|
||||||
atl1c_trans_high = 1
|
|
||||||
};
|
|
||||||
|
|
||||||
struct atl1c_hw_stats {
|
|
||||||
/* rx */
|
|
||||||
unsigned long rx_ok; /* The number of good packet received. */
|
|
||||||
unsigned long rx_bcast; /* The number of good broadcast packet received. */
|
|
||||||
unsigned long rx_mcast; /* The number of good multicast packet received. */
|
|
||||||
unsigned long rx_pause; /* The number of Pause packet received. */
|
|
||||||
unsigned long rx_ctrl; /* The number of Control packet received other than Pause frame. */
|
|
||||||
unsigned long rx_fcs_err; /* The number of packets with bad FCS. */
|
|
||||||
unsigned long rx_len_err; /* The number of packets with mismatch of length field and actual size. */
|
|
||||||
unsigned long rx_byte_cnt; /* The number of bytes of good packet received. FCS is NOT included. */
|
|
||||||
unsigned long rx_runt; /* The number of packets received that are less than 64 byte long and with good FCS. */
|
|
||||||
unsigned long rx_frag; /* The number of packets received that are less than 64 byte long and with bad FCS. */
|
|
||||||
unsigned long rx_sz_64; /* The number of good and bad packets received that are 64 byte long. */
|
|
||||||
unsigned long rx_sz_65_127; /* The number of good and bad packets received that are between 65 and 127-byte long. */
|
|
||||||
unsigned long rx_sz_128_255; /* The number of good and bad packets received that are between 128 and 255-byte long. */
|
|
||||||
unsigned long rx_sz_256_511; /* The number of good and bad packets received that are between 256 and 511-byte long. */
|
|
||||||
unsigned long rx_sz_512_1023; /* The number of good and bad packets received that are between 512 and 1023-byte long. */
|
|
||||||
unsigned long rx_sz_1024_1518; /* The number of good and bad packets received that are between 1024 and 1518-byte long. */
|
|
||||||
unsigned long rx_sz_1519_max; /* The number of good and bad packets received that are between 1519-byte and MTU. */
|
|
||||||
unsigned long rx_sz_ov; /* The number of good and bad packets received that are more than MTU size truncated by Selene. */
|
|
||||||
unsigned long rx_rxf_ov; /* The number of frame dropped due to occurrence of RX FIFO overflow. */
|
|
||||||
unsigned long rx_rrd_ov; /* The number of frame dropped due to occurrence of RRD overflow. */
|
|
||||||
unsigned long rx_align_err; /* Alignment Error */
|
|
||||||
unsigned long rx_bcast_byte_cnt; /* The byte count of broadcast packet received, excluding FCS. */
|
|
||||||
unsigned long rx_mcast_byte_cnt; /* The byte count of multicast packet received, excluding FCS. */
|
|
||||||
unsigned long rx_err_addr; /* The number of packets dropped due to address filtering. */
|
|
||||||
|
|
||||||
/* tx */
|
|
||||||
unsigned long tx_ok; /* The number of good packet transmitted. */
|
|
||||||
unsigned long tx_bcast; /* The number of good broadcast packet transmitted. */
|
|
||||||
unsigned long tx_mcast; /* The number of good multicast packet transmitted. */
|
|
||||||
unsigned long tx_pause; /* The number of Pause packet transmitted. */
|
|
||||||
unsigned long tx_exc_defer; /* The number of packets transmitted with excessive deferral. */
|
|
||||||
unsigned long tx_ctrl; /* The number of packets transmitted is a control frame, excluding Pause frame. */
|
|
||||||
unsigned long tx_defer; /* The number of packets transmitted that is deferred. */
|
|
||||||
unsigned long tx_byte_cnt; /* The number of bytes of data transmitted. FCS is NOT included. */
|
|
||||||
unsigned long tx_sz_64; /* The number of good and bad packets transmitted that are 64 byte long. */
|
|
||||||
unsigned long tx_sz_65_127; /* The number of good and bad packets transmitted that are between 65 and 127-byte long. */
|
|
||||||
unsigned long tx_sz_128_255; /* The number of good and bad packets transmitted that are between 128 and 255-byte long. */
|
|
||||||
unsigned long tx_sz_256_511; /* The number of good and bad packets transmitted that are between 256 and 511-byte long. */
|
|
||||||
unsigned long tx_sz_512_1023; /* The number of good and bad packets transmitted that are between 512 and 1023-byte long. */
|
|
||||||
unsigned long tx_sz_1024_1518; /* The number of good and bad packets transmitted that are between 1024 and 1518-byte long. */
|
|
||||||
unsigned long tx_sz_1519_max; /* The number of good and bad packets transmitted that are between 1519-byte and MTU. */
|
|
||||||
unsigned long tx_1_col; /* The number of packets subsequently transmitted successfully with a single prior collision. */
|
|
||||||
unsigned long tx_2_col; /* The number of packets subsequently transmitted successfully with multiple prior collisions. */
|
|
||||||
unsigned long tx_late_col; /* The number of packets transmitted with late collisions. */
|
|
||||||
unsigned long tx_abort_col; /* The number of transmit packets aborted due to excessive collisions. */
|
|
||||||
unsigned long tx_underrun; /* The number of transmit packets aborted due to transmit FIFO underrun, or TRD FIFO underrun */
|
|
||||||
unsigned long tx_rd_eop; /* The number of times that read beyond the EOP into the next frame area when TRD was not written timely */
|
|
||||||
unsigned long tx_len_err; /* The number of transmit packets with length field does NOT match the actual frame size. */
|
|
||||||
unsigned long tx_trunc; /* The number of transmit packets truncated due to size exceeding MTU. */
|
|
||||||
unsigned long tx_bcast_byte; /* The byte count of broadcast packet transmitted, excluding FCS. */
|
|
||||||
unsigned long tx_mcast_byte; /* The byte count of multicast packet transmitted, excluding FCS. */
|
|
||||||
};
|
|
||||||
|
|
||||||
struct atl1c_hw {
|
|
||||||
u8 __iomem *hw_addr; /* inner register address */
|
|
||||||
struct atl1c_adapter *adapter;
|
|
||||||
enum atl1c_nic_type nic_type;
|
|
||||||
enum atl1c_dma_order dma_order;
|
|
||||||
enum atl1c_dma_rcb rcb_value;
|
|
||||||
enum atl1c_dma_req_block dmar_block;
|
|
||||||
|
|
||||||
u16 device_id;
|
|
||||||
u16 vendor_id;
|
|
||||||
u16 subsystem_id;
|
|
||||||
u16 subsystem_vendor_id;
|
|
||||||
u8 revision_id;
|
|
||||||
u16 phy_id1;
|
|
||||||
u16 phy_id2;
|
|
||||||
|
|
||||||
u32 intr_mask;
|
|
||||||
|
|
||||||
u8 preamble_len;
|
|
||||||
u16 max_frame_size;
|
|
||||||
u16 min_frame_size;
|
|
||||||
|
|
||||||
enum atl1c_mac_speed mac_speed;
|
|
||||||
bool mac_duplex;
|
|
||||||
bool hibernate;
|
|
||||||
u16 media_type;
|
|
||||||
#define MEDIA_TYPE_AUTO_SENSOR 0
|
|
||||||
#define MEDIA_TYPE_100M_FULL 1
|
|
||||||
#define MEDIA_TYPE_100M_HALF 2
|
|
||||||
#define MEDIA_TYPE_10M_FULL 3
|
|
||||||
#define MEDIA_TYPE_10M_HALF 4
|
|
||||||
|
|
||||||
u16 autoneg_advertised;
|
|
||||||
u16 mii_autoneg_adv_reg;
|
|
||||||
u16 mii_1000t_ctrl_reg;
|
|
||||||
|
|
||||||
u16 tx_imt; /* TX Interrupt Moderator timer ( 2us resolution) */
|
|
||||||
u16 rx_imt; /* RX Interrupt Moderator timer ( 2us resolution) */
|
|
||||||
u16 ict; /* Interrupt Clear timer (2us resolution) */
|
|
||||||
u16 ctrl_flags;
|
|
||||||
#define ATL1C_INTR_CLEAR_ON_READ 0x0001
|
|
||||||
#define ATL1C_INTR_MODRT_ENABLE 0x0002
|
|
||||||
#define ATL1C_CMB_ENABLE 0x0004
|
|
||||||
#define ATL1C_SMB_ENABLE 0x0010
|
|
||||||
#define ATL1C_TXQ_MODE_ENHANCE 0x0020
|
|
||||||
#define ATL1C_RX_IPV6_CHKSUM 0x0040
|
|
||||||
#define ATL1C_ASPM_L0S_SUPPORT 0x0080
|
|
||||||
#define ATL1C_ASPM_L1_SUPPORT 0x0100
|
|
||||||
#define ATL1C_ASPM_CTRL_MON 0x0200
|
|
||||||
#define ATL1C_HIB_DISABLE 0x0400
|
|
||||||
#define ATL1C_APS_MODE_ENABLE 0x0800
|
|
||||||
#define ATL1C_LINK_EXT_SYNC 0x1000
|
|
||||||
#define ATL1C_CLK_GATING_EN 0x2000
|
|
||||||
#define ATL1C_FPGA_VERSION 0x8000
|
|
||||||
u16 link_cap_flags;
|
|
||||||
#define ATL1C_LINK_CAP_1000M 0x0001
|
|
||||||
u32 smb_timer;
|
|
||||||
|
|
||||||
u16 rrd_thresh; /* Threshold of number of RRD produced to trigger
|
|
||||||
interrupt request */
|
|
||||||
u16 tpd_thresh;
|
|
||||||
u8 tpd_burst; /* Number of TPD to prefetch in cache-aligned burst. */
|
|
||||||
u8 rfd_burst;
|
|
||||||
u32 base_cpu;
|
|
||||||
u32 indirect_tab;
|
|
||||||
u8 mac_addr[ETH_ALEN];
|
|
||||||
u8 perm_mac_addr[ETH_ALEN];
|
|
||||||
|
|
||||||
bool phy_configured;
|
|
||||||
bool re_autoneg;
|
|
||||||
bool emi_ca;
|
|
||||||
bool msi_lnkpatch; /* link patch for specific platforms */
|
|
||||||
};
|
|
||||||
|
|
||||||
/*
|
|
||||||
* atl1c_ring_header represents a single, contiguous block of DMA space
|
|
||||||
* mapped for the three descriptor rings (tpd, rfd, rrd) described below
|
|
||||||
*/
|
|
||||||
struct atl1c_ring_header {
|
|
||||||
void *desc; /* virtual address */
|
|
||||||
dma_addr_t dma; /* physical address*/
|
|
||||||
unsigned int size; /* length in bytes */
|
|
||||||
};
|
|
||||||
|
|
||||||
/*
|
|
||||||
* atl1c_buffer is wrapper around a pointer to a socket buffer
|
|
||||||
* so a DMA handle can be stored along with the skb
|
|
||||||
*/
|
|
||||||
struct atl1c_buffer {
|
|
||||||
struct sk_buff *skb; /* socket buffer */
|
|
||||||
u16 length; /* rx buffer length */
|
|
||||||
u16 flags; /* information of buffer */
|
|
||||||
#define ATL1C_BUFFER_FREE 0x0001
|
|
||||||
#define ATL1C_BUFFER_BUSY 0x0002
|
|
||||||
#define ATL1C_BUFFER_STATE_MASK 0x0003
|
|
||||||
|
|
||||||
#define ATL1C_PCIMAP_SINGLE 0x0004
|
|
||||||
#define ATL1C_PCIMAP_PAGE 0x0008
|
|
||||||
#define ATL1C_PCIMAP_TYPE_MASK 0x000C
|
|
||||||
|
|
||||||
#define ATL1C_PCIMAP_TODEVICE 0x0010
|
|
||||||
#define ATL1C_PCIMAP_FROMDEVICE 0x0020
|
|
||||||
#define ATL1C_PCIMAP_DIRECTION_MASK 0x0030
|
|
||||||
dma_addr_t dma;
|
|
||||||
};
|
|
||||||
|
|
||||||
#define ATL1C_SET_BUFFER_STATE(buff, state) do { \
|
|
||||||
((buff)->flags) &= ~ATL1C_BUFFER_STATE_MASK; \
|
|
||||||
((buff)->flags) |= (state); \
|
|
||||||
} while (0)
|
|
||||||
|
|
||||||
#define ATL1C_SET_PCIMAP_TYPE(buff, type, direction) do { \
|
|
||||||
((buff)->flags) &= ~ATL1C_PCIMAP_TYPE_MASK; \
|
|
||||||
((buff)->flags) |= (type); \
|
|
||||||
((buff)->flags) &= ~ATL1C_PCIMAP_DIRECTION_MASK; \
|
|
||||||
((buff)->flags) |= (direction); \
|
|
||||||
} while (0)
|
|
||||||
|
|
||||||
/* transimit packet descriptor (tpd) ring */
|
|
||||||
struct atl1c_tpd_ring {
|
|
||||||
void *desc; /* descriptor ring virtual address */
|
|
||||||
dma_addr_t dma; /* descriptor ring physical address */
|
|
||||||
u16 size; /* descriptor ring length in bytes */
|
|
||||||
u16 count; /* number of descriptors in the ring */
|
|
||||||
u16 next_to_use; /* this is protectd by adapter->tx_lock */
|
|
||||||
atomic_t next_to_clean;
|
|
||||||
struct atl1c_buffer *buffer_info;
|
|
||||||
};
|
|
||||||
|
|
||||||
/* receive free descriptor (rfd) ring */
|
|
||||||
struct atl1c_rfd_ring {
|
|
||||||
void *desc; /* descriptor ring virtual address */
|
|
||||||
dma_addr_t dma; /* descriptor ring physical address */
|
|
||||||
u16 size; /* descriptor ring length in bytes */
|
|
||||||
u16 count; /* number of descriptors in the ring */
|
|
||||||
u16 next_to_use;
|
|
||||||
u16 next_to_clean;
|
|
||||||
struct atl1c_buffer *buffer_info;
|
|
||||||
};
|
|
||||||
|
|
||||||
/* receive return descriptor (rrd) ring */
|
|
||||||
struct atl1c_rrd_ring {
|
|
||||||
void *desc; /* descriptor ring virtual address */
|
|
||||||
dma_addr_t dma; /* descriptor ring physical address */
|
|
||||||
u16 size; /* descriptor ring length in bytes */
|
|
||||||
u16 count; /* number of descriptors in the ring */
|
|
||||||
u16 next_to_use;
|
|
||||||
u16 next_to_clean;
|
|
||||||
};
|
|
||||||
|
|
||||||
/* board specific private data structure */
|
|
||||||
struct atl1c_adapter {
|
|
||||||
struct net_device *netdev;
|
|
||||||
struct pci_dev *pdev;
|
|
||||||
struct napi_struct napi;
|
|
||||||
struct page *rx_page;
|
|
||||||
unsigned int rx_page_offset;
|
|
||||||
unsigned int rx_frag_size;
|
|
||||||
struct atl1c_hw hw;
|
|
||||||
struct atl1c_hw_stats hw_stats;
|
|
||||||
struct mii_if_info mii; /* MII interface info */
|
|
||||||
u16 rx_buffer_len;
|
|
||||||
|
|
||||||
unsigned long flags;
|
|
||||||
#define __AT_TESTING 0x0001
|
|
||||||
#define __AT_RESETTING 0x0002
|
|
||||||
#define __AT_DOWN 0x0003
|
|
||||||
unsigned long work_event;
|
|
||||||
#define ATL1C_WORK_EVENT_RESET 0
|
|
||||||
#define ATL1C_WORK_EVENT_LINK_CHANGE 1
|
|
||||||
u32 msg_enable;
|
|
||||||
|
|
||||||
bool have_msi;
|
|
||||||
u32 wol;
|
|
||||||
u16 link_speed;
|
|
||||||
u16 link_duplex;
|
|
||||||
|
|
||||||
spinlock_t mdio_lock;
|
|
||||||
spinlock_t tx_lock;
|
|
||||||
atomic_t irq_sem;
|
|
||||||
|
|
||||||
struct work_struct common_task;
|
|
||||||
struct timer_list watchdog_timer;
|
|
||||||
struct timer_list phy_config_timer;
|
|
||||||
|
|
||||||
/* All Descriptor memory */
|
|
||||||
struct atl1c_ring_header ring_header;
|
|
||||||
struct atl1c_tpd_ring tpd_ring[AT_MAX_TRANSMIT_QUEUE];
|
|
||||||
struct atl1c_rfd_ring rfd_ring;
|
|
||||||
struct atl1c_rrd_ring rrd_ring;
|
|
||||||
u32 bd_number; /* board number;*/
|
|
||||||
};
|
|
||||||
|
|
||||||
#define AT_WRITE_REG(a, reg, value) ( \
|
|
||||||
writel((value), ((a)->hw_addr + reg)))
|
|
||||||
|
|
||||||
#define AT_WRITE_FLUSH(a) (\
|
|
||||||
readl((a)->hw_addr))
|
|
||||||
|
|
||||||
#define AT_READ_REG(a, reg, pdata) do { \
|
|
||||||
if (unlikely((a)->hibernate)) { \
|
|
||||||
readl((a)->hw_addr + reg); \
|
|
||||||
*(u32 *)pdata = readl((a)->hw_addr + reg); \
|
|
||||||
} else { \
|
|
||||||
*(u32 *)pdata = readl((a)->hw_addr + reg); \
|
|
||||||
} \
|
|
||||||
} while (0)
|
|
||||||
|
|
||||||
#define AT_WRITE_REGB(a, reg, value) (\
|
|
||||||
writeb((value), ((a)->hw_addr + reg)))
|
|
||||||
|
|
||||||
#define AT_READ_REGB(a, reg) (\
|
|
||||||
readb((a)->hw_addr + reg))
|
|
||||||
|
|
||||||
#define AT_WRITE_REGW(a, reg, value) (\
|
|
||||||
writew((value), ((a)->hw_addr + reg)))
|
|
||||||
|
|
||||||
#define AT_READ_REGW(a, reg, pdata) do { \
|
|
||||||
if (unlikely((a)->hibernate)) { \
|
|
||||||
readw((a)->hw_addr + reg); \
|
|
||||||
*(u16 *)pdata = readw((a)->hw_addr + reg); \
|
|
||||||
} else { \
|
|
||||||
*(u16 *)pdata = readw((a)->hw_addr + reg); \
|
|
||||||
} \
|
|
||||||
} while (0)
|
|
||||||
|
|
||||||
#define AT_WRITE_REG_ARRAY(a, reg, offset, value) ( \
|
|
||||||
writel((value), (((a)->hw_addr + reg) + ((offset) << 2))))
|
|
||||||
|
|
||||||
#define AT_READ_REG_ARRAY(a, reg, offset) ( \
|
|
||||||
readl(((a)->hw_addr + reg) + ((offset) << 2)))
|
|
||||||
|
|
||||||
extern char atl1c_driver_name[];
|
|
||||||
extern char atl1c_driver_version[];
|
|
||||||
|
|
||||||
void atl1c_reinit_locked(struct atl1c_adapter *adapter);
|
|
||||||
s32 atl1c_reset_hw(struct atl1c_hw *hw);
|
|
||||||
void atl1c_set_ethtool_ops(struct net_device *netdev);
|
|
||||||
#endif /* _ATL1C_H_ */
|
|
@ -1,305 +0,0 @@
|
|||||||
/*
|
|
||||||
* Copyright(c) 2009 - 2009 Atheros Corporation. All rights reserved.
|
|
||||||
*
|
|
||||||
* Derived from Intel e1000 driver
|
|
||||||
* Copyright(c) 1999 - 2005 Intel Corporation. All rights reserved.
|
|
||||||
*
|
|
||||||
* This program is free software; you can redistribute it and/or modify it
|
|
||||||
* under the terms of the GNU General Public License as published by the Free
|
|
||||||
* Software Foundation; either version 2 of the License, or (at your option)
|
|
||||||
* any later version.
|
|
||||||
*
|
|
||||||
* This program is distributed in the hope that it will be useful, but WITHOUT
|
|
||||||
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
|
|
||||||
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
|
|
||||||
* more details.
|
|
||||||
*
|
|
||||||
* You should have received a copy of the GNU General Public License along with
|
|
||||||
* this program; if not, write to the Free Software Foundation, Inc., 59
|
|
||||||
* Temple Place - Suite 330, Boston, MA 02111-1307, USA.
|
|
||||||
*
|
|
||||||
*/
|
|
||||||
|
|
||||||
#include <linux/netdevice.h>
|
|
||||||
#include <linux/ethtool.h>
|
|
||||||
#include <linux/slab.h>
|
|
||||||
|
|
||||||
#include "atl1c.h"
|
|
||||||
|
|
||||||
static int atl1c_get_settings(struct net_device *netdev,
|
|
||||||
struct ethtool_cmd *ecmd)
|
|
||||||
{
|
|
||||||
struct atl1c_adapter *adapter = netdev_priv(netdev);
|
|
||||||
struct atl1c_hw *hw = &adapter->hw;
|
|
||||||
|
|
||||||
ecmd->supported = (SUPPORTED_10baseT_Half |
|
|
||||||
SUPPORTED_10baseT_Full |
|
|
||||||
SUPPORTED_100baseT_Half |
|
|
||||||
SUPPORTED_100baseT_Full |
|
|
||||||
SUPPORTED_Autoneg |
|
|
||||||
SUPPORTED_TP);
|
|
||||||
if (hw->link_cap_flags & ATL1C_LINK_CAP_1000M)
|
|
||||||
ecmd->supported |= SUPPORTED_1000baseT_Full;
|
|
||||||
|
|
||||||
ecmd->advertising = ADVERTISED_TP;
|
|
||||||
|
|
||||||
ecmd->advertising |= hw->autoneg_advertised;
|
|
||||||
|
|
||||||
ecmd->port = PORT_TP;
|
|
||||||
ecmd->phy_address = 0;
|
|
||||||
ecmd->transceiver = XCVR_INTERNAL;
|
|
||||||
|
|
||||||
if (adapter->link_speed != SPEED_0) {
|
|
||||||
ethtool_cmd_speed_set(ecmd, adapter->link_speed);
|
|
||||||
if (adapter->link_duplex == FULL_DUPLEX)
|
|
||||||
ecmd->duplex = DUPLEX_FULL;
|
|
||||||
else
|
|
||||||
ecmd->duplex = DUPLEX_HALF;
|
|
||||||
} else {
|
|
||||||
ethtool_cmd_speed_set(ecmd, SPEED_UNKNOWN);
|
|
||||||
ecmd->duplex = DUPLEX_UNKNOWN;
|
|
||||||
}
|
|
||||||
|
|
||||||
ecmd->autoneg = AUTONEG_ENABLE;
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
static int atl1c_set_settings(struct net_device *netdev,
|
|
||||||
struct ethtool_cmd *ecmd)
|
|
||||||
{
|
|
||||||
struct atl1c_adapter *adapter = netdev_priv(netdev);
|
|
||||||
struct atl1c_hw *hw = &adapter->hw;
|
|
||||||
u16 autoneg_advertised;
|
|
||||||
|
|
||||||
while (test_and_set_bit(__AT_RESETTING, &adapter->flags))
|
|
||||||
msleep(1);
|
|
||||||
|
|
||||||
if (ecmd->autoneg == AUTONEG_ENABLE) {
|
|
||||||
autoneg_advertised = ADVERTISED_Autoneg;
|
|
||||||
} else {
|
|
||||||
u32 speed = ethtool_cmd_speed(ecmd);
|
|
||||||
if (speed == SPEED_1000) {
|
|
||||||
if (ecmd->duplex != DUPLEX_FULL) {
|
|
||||||
if (netif_msg_link(adapter))
|
|
||||||
dev_warn(&adapter->pdev->dev,
|
|
||||||
"1000M half is invalid\n");
|
|
||||||
clear_bit(__AT_RESETTING, &adapter->flags);
|
|
||||||
return -EINVAL;
|
|
||||||
}
|
|
||||||
autoneg_advertised = ADVERTISED_1000baseT_Full;
|
|
||||||
} else if (speed == SPEED_100) {
|
|
||||||
if (ecmd->duplex == DUPLEX_FULL)
|
|
||||||
autoneg_advertised = ADVERTISED_100baseT_Full;
|
|
||||||
else
|
|
||||||
autoneg_advertised = ADVERTISED_100baseT_Half;
|
|
||||||
} else {
|
|
||||||
if (ecmd->duplex == DUPLEX_FULL)
|
|
||||||
autoneg_advertised = ADVERTISED_10baseT_Full;
|
|
||||||
else
|
|
||||||
autoneg_advertised = ADVERTISED_10baseT_Half;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if (hw->autoneg_advertised != autoneg_advertised) {
|
|
||||||
hw->autoneg_advertised = autoneg_advertised;
|
|
||||||
if (atl1c_restart_autoneg(hw) != 0) {
|
|
||||||
if (netif_msg_link(adapter))
|
|
||||||
dev_warn(&adapter->pdev->dev,
|
|
||||||
"ethtool speed/duplex setting failed\n");
|
|
||||||
clear_bit(__AT_RESETTING, &adapter->flags);
|
|
||||||
return -EINVAL;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
clear_bit(__AT_RESETTING, &adapter->flags);
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
static u32 atl1c_get_msglevel(struct net_device *netdev)
|
|
||||||
{
|
|
||||||
struct atl1c_adapter *adapter = netdev_priv(netdev);
|
|
||||||
return adapter->msg_enable;
|
|
||||||
}
|
|
||||||
|
|
||||||
static void atl1c_set_msglevel(struct net_device *netdev, u32 data)
|
|
||||||
{
|
|
||||||
struct atl1c_adapter *adapter = netdev_priv(netdev);
|
|
||||||
adapter->msg_enable = data;
|
|
||||||
}
|
|
||||||
|
|
||||||
static int atl1c_get_regs_len(struct net_device *netdev)
|
|
||||||
{
|
|
||||||
return AT_REGS_LEN;
|
|
||||||
}
|
|
||||||
|
|
||||||
static void atl1c_get_regs(struct net_device *netdev,
|
|
||||||
struct ethtool_regs *regs, void *p)
|
|
||||||
{
|
|
||||||
struct atl1c_adapter *adapter = netdev_priv(netdev);
|
|
||||||
struct atl1c_hw *hw = &adapter->hw;
|
|
||||||
u32 *regs_buff = p;
|
|
||||||
u16 phy_data;
|
|
||||||
|
|
||||||
memset(p, 0, AT_REGS_LEN);
|
|
||||||
|
|
||||||
regs->version = 1;
|
|
||||||
AT_READ_REG(hw, REG_PM_CTRL, p++);
|
|
||||||
AT_READ_REG(hw, REG_MAC_HALF_DUPLX_CTRL, p++);
|
|
||||||
AT_READ_REG(hw, REG_TWSI_CTRL, p++);
|
|
||||||
AT_READ_REG(hw, REG_PCIE_DEV_MISC_CTRL, p++);
|
|
||||||
AT_READ_REG(hw, REG_MASTER_CTRL, p++);
|
|
||||||
AT_READ_REG(hw, REG_MANUAL_TIMER_INIT, p++);
|
|
||||||
AT_READ_REG(hw, REG_IRQ_MODRT_TIMER_INIT, p++);
|
|
||||||
AT_READ_REG(hw, REG_GPHY_CTRL, p++);
|
|
||||||
AT_READ_REG(hw, REG_LINK_CTRL, p++);
|
|
||||||
AT_READ_REG(hw, REG_IDLE_STATUS, p++);
|
|
||||||
AT_READ_REG(hw, REG_MDIO_CTRL, p++);
|
|
||||||
AT_READ_REG(hw, REG_SERDES, p++);
|
|
||||||
AT_READ_REG(hw, REG_MAC_CTRL, p++);
|
|
||||||
AT_READ_REG(hw, REG_MAC_IPG_IFG, p++);
|
|
||||||
AT_READ_REG(hw, REG_MAC_STA_ADDR, p++);
|
|
||||||
AT_READ_REG(hw, REG_MAC_STA_ADDR+4, p++);
|
|
||||||
AT_READ_REG(hw, REG_RX_HASH_TABLE, p++);
|
|
||||||
AT_READ_REG(hw, REG_RX_HASH_TABLE+4, p++);
|
|
||||||
AT_READ_REG(hw, REG_RXQ_CTRL, p++);
|
|
||||||
AT_READ_REG(hw, REG_TXQ_CTRL, p++);
|
|
||||||
AT_READ_REG(hw, REG_MTU, p++);
|
|
||||||
AT_READ_REG(hw, REG_WOL_CTRL, p++);
|
|
||||||
|
|
||||||
atl1c_read_phy_reg(hw, MII_BMCR, &phy_data);
|
|
||||||
regs_buff[AT_REGS_LEN/sizeof(u32) - 2] = (u32) phy_data;
|
|
||||||
atl1c_read_phy_reg(hw, MII_BMSR, &phy_data);
|
|
||||||
regs_buff[AT_REGS_LEN/sizeof(u32) - 1] = (u32) phy_data;
|
|
||||||
}
|
|
||||||
|
|
||||||
static int atl1c_get_eeprom_len(struct net_device *netdev)
|
|
||||||
{
|
|
||||||
struct atl1c_adapter *adapter = netdev_priv(netdev);
|
|
||||||
|
|
||||||
if (atl1c_check_eeprom_exist(&adapter->hw))
|
|
||||||
return AT_EEPROM_LEN;
|
|
||||||
else
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
static int atl1c_get_eeprom(struct net_device *netdev,
|
|
||||||
struct ethtool_eeprom *eeprom, u8 *bytes)
|
|
||||||
{
|
|
||||||
struct atl1c_adapter *adapter = netdev_priv(netdev);
|
|
||||||
struct atl1c_hw *hw = &adapter->hw;
|
|
||||||
u32 *eeprom_buff;
|
|
||||||
int first_dword, last_dword;
|
|
||||||
int ret_val = 0;
|
|
||||||
int i;
|
|
||||||
|
|
||||||
if (eeprom->len == 0)
|
|
||||||
return -EINVAL;
|
|
||||||
|
|
||||||
if (!atl1c_check_eeprom_exist(hw)) /* not exist */
|
|
||||||
return -EINVAL;
|
|
||||||
|
|
||||||
eeprom->magic = adapter->pdev->vendor |
|
|
||||||
(adapter->pdev->device << 16);
|
|
||||||
|
|
||||||
first_dword = eeprom->offset >> 2;
|
|
||||||
last_dword = (eeprom->offset + eeprom->len - 1) >> 2;
|
|
||||||
|
|
||||||
eeprom_buff = kmalloc(sizeof(u32) *
|
|
||||||
(last_dword - first_dword + 1), GFP_KERNEL);
|
|
||||||
if (eeprom_buff == NULL)
|
|
||||||
return -ENOMEM;
|
|
||||||
|
|
||||||
for (i = first_dword; i < last_dword; i++) {
|
|
||||||
if (!atl1c_read_eeprom(hw, i * 4, &(eeprom_buff[i-first_dword]))) {
|
|
||||||
kfree(eeprom_buff);
|
|
||||||
return -EIO;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
memcpy(bytes, (u8 *)eeprom_buff + (eeprom->offset & 3),
|
|
||||||
eeprom->len);
|
|
||||||
kfree(eeprom_buff);
|
|
||||||
|
|
||||||
return ret_val;
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
static void atl1c_get_drvinfo(struct net_device *netdev,
|
|
||||||
struct ethtool_drvinfo *drvinfo)
|
|
||||||
{
|
|
||||||
struct atl1c_adapter *adapter = netdev_priv(netdev);
|
|
||||||
|
|
||||||
strlcpy(drvinfo->driver, atl1c_driver_name, sizeof(drvinfo->driver));
|
|
||||||
strlcpy(drvinfo->version, atl1c_driver_version,
|
|
||||||
sizeof(drvinfo->version));
|
|
||||||
strlcpy(drvinfo->bus_info, pci_name(adapter->pdev),
|
|
||||||
sizeof(drvinfo->bus_info));
|
|
||||||
}
|
|
||||||
|
|
||||||
static void atl1c_get_wol(struct net_device *netdev,
|
|
||||||
struct ethtool_wolinfo *wol)
|
|
||||||
{
|
|
||||||
struct atl1c_adapter *adapter = netdev_priv(netdev);
|
|
||||||
|
|
||||||
wol->supported = WAKE_MAGIC | WAKE_PHY;
|
|
||||||
wol->wolopts = 0;
|
|
||||||
|
|
||||||
if (adapter->wol & AT_WUFC_EX)
|
|
||||||
wol->wolopts |= WAKE_UCAST;
|
|
||||||
if (adapter->wol & AT_WUFC_MC)
|
|
||||||
wol->wolopts |= WAKE_MCAST;
|
|
||||||
if (adapter->wol & AT_WUFC_BC)
|
|
||||||
wol->wolopts |= WAKE_BCAST;
|
|
||||||
if (adapter->wol & AT_WUFC_MAG)
|
|
||||||
wol->wolopts |= WAKE_MAGIC;
|
|
||||||
if (adapter->wol & AT_WUFC_LNKC)
|
|
||||||
wol->wolopts |= WAKE_PHY;
|
|
||||||
}
|
|
||||||
|
|
||||||
static int atl1c_set_wol(struct net_device *netdev, struct ethtool_wolinfo *wol)
|
|
||||||
{
|
|
||||||
struct atl1c_adapter *adapter = netdev_priv(netdev);
|
|
||||||
|
|
||||||
if (wol->wolopts & (WAKE_ARP | WAKE_MAGICSECURE |
|
|
||||||
WAKE_UCAST | WAKE_BCAST | WAKE_MCAST))
|
|
||||||
return -EOPNOTSUPP;
|
|
||||||
/* these settings will always override what we currently have */
|
|
||||||
adapter->wol = 0;
|
|
||||||
|
|
||||||
if (wol->wolopts & WAKE_MAGIC)
|
|
||||||
adapter->wol |= AT_WUFC_MAG;
|
|
||||||
if (wol->wolopts & WAKE_PHY)
|
|
||||||
adapter->wol |= AT_WUFC_LNKC;
|
|
||||||
|
|
||||||
device_set_wakeup_enable(&adapter->pdev->dev, adapter->wol);
|
|
||||||
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
static int atl1c_nway_reset(struct net_device *netdev)
|
|
||||||
{
|
|
||||||
struct atl1c_adapter *adapter = netdev_priv(netdev);
|
|
||||||
if (netif_running(netdev))
|
|
||||||
atl1c_reinit_locked(adapter);
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
static const struct ethtool_ops atl1c_ethtool_ops = {
|
|
||||||
.get_settings = atl1c_get_settings,
|
|
||||||
.set_settings = atl1c_set_settings,
|
|
||||||
.get_drvinfo = atl1c_get_drvinfo,
|
|
||||||
.get_regs_len = atl1c_get_regs_len,
|
|
||||||
.get_regs = atl1c_get_regs,
|
|
||||||
.get_wol = atl1c_get_wol,
|
|
||||||
.set_wol = atl1c_set_wol,
|
|
||||||
.get_msglevel = atl1c_get_msglevel,
|
|
||||||
.set_msglevel = atl1c_set_msglevel,
|
|
||||||
.nway_reset = atl1c_nway_reset,
|
|
||||||
.get_link = ethtool_op_get_link,
|
|
||||||
.get_eeprom_len = atl1c_get_eeprom_len,
|
|
||||||
.get_eeprom = atl1c_get_eeprom,
|
|
||||||
};
|
|
||||||
|
|
||||||
void atl1c_set_ethtool_ops(struct net_device *netdev)
|
|
||||||
{
|
|
||||||
netdev->ethtool_ops = &atl1c_ethtool_ops;
|
|
||||||
}
|
|
@ -1,863 +0,0 @@
|
|||||||
/*
|
|
||||||
* Copyright(c) 2007 Atheros Corporation. All rights reserved.
|
|
||||||
*
|
|
||||||
* Derived from Intel e1000 driver
|
|
||||||
* Copyright(c) 1999 - 2005 Intel Corporation. All rights reserved.
|
|
||||||
*
|
|
||||||
* This program is free software; you can redistribute it and/or modify it
|
|
||||||
* under the terms of the GNU General Public License as published by the Free
|
|
||||||
* Software Foundation; either version 2 of the License, or (at your option)
|
|
||||||
* any later version.
|
|
||||||
*
|
|
||||||
* This program is distributed in the hope that it will be useful, but WITHOUT
|
|
||||||
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
|
|
||||||
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
|
|
||||||
* more details.
|
|
||||||
*
|
|
||||||
* You should have received a copy of the GNU General Public License along with
|
|
||||||
* this program; if not, write to the Free Software Foundation, Inc., 59
|
|
||||||
* Temple Place - Suite 330, Boston, MA 02111-1307, USA.
|
|
||||||
*/
|
|
||||||
#include <linux/pci.h>
|
|
||||||
#include <linux/delay.h>
|
|
||||||
#include <linux/mii.h>
|
|
||||||
#include <linux/crc32.h>
|
|
||||||
|
|
||||||
#include "atl1c.h"
|
|
||||||
|
|
||||||
/*
|
|
||||||
* check_eeprom_exist
|
|
||||||
* return 1 if eeprom exist
|
|
||||||
*/
|
|
||||||
int atl1c_check_eeprom_exist(struct atl1c_hw *hw)
|
|
||||||
{
|
|
||||||
u32 data;
|
|
||||||
|
|
||||||
AT_READ_REG(hw, REG_TWSI_DEBUG, &data);
|
|
||||||
if (data & TWSI_DEBUG_DEV_EXIST)
|
|
||||||
return 1;
|
|
||||||
|
|
||||||
AT_READ_REG(hw, REG_MASTER_CTRL, &data);
|
|
||||||
if (data & MASTER_CTRL_OTP_SEL)
|
|
||||||
return 1;
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
void atl1c_hw_set_mac_addr(struct atl1c_hw *hw, u8 *mac_addr)
|
|
||||||
{
|
|
||||||
u32 value;
|
|
||||||
/*
|
|
||||||
* 00-0B-6A-F6-00-DC
|
|
||||||
* 0: 6AF600DC 1: 000B
|
|
||||||
* low dword
|
|
||||||
*/
|
|
||||||
value = mac_addr[2] << 24 |
|
|
||||||
mac_addr[3] << 16 |
|
|
||||||
mac_addr[4] << 8 |
|
|
||||||
mac_addr[5];
|
|
||||||
AT_WRITE_REG_ARRAY(hw, REG_MAC_STA_ADDR, 0, value);
|
|
||||||
/* hight dword */
|
|
||||||
value = mac_addr[0] << 8 |
|
|
||||||
mac_addr[1];
|
|
||||||
AT_WRITE_REG_ARRAY(hw, REG_MAC_STA_ADDR, 1, value);
|
|
||||||
}
|
|
||||||
|
|
||||||
/* read mac address from hardware register */
|
|
||||||
static bool atl1c_read_current_addr(struct atl1c_hw *hw, u8 *eth_addr)
|
|
||||||
{
|
|
||||||
u32 addr[2];
|
|
||||||
|
|
||||||
AT_READ_REG(hw, REG_MAC_STA_ADDR, &addr[0]);
|
|
||||||
AT_READ_REG(hw, REG_MAC_STA_ADDR + 4, &addr[1]);
|
|
||||||
|
|
||||||
*(u32 *) ð_addr[2] = htonl(addr[0]);
|
|
||||||
*(u16 *) ð_addr[0] = htons((u16)addr[1]);
|
|
||||||
|
|
||||||
return is_valid_ether_addr(eth_addr);
|
|
||||||
}
|
|
||||||
|
|
||||||
/*
|
|
||||||
* atl1c_get_permanent_address
|
|
||||||
* return 0 if get valid mac address,
|
|
||||||
*/
|
|
||||||
static int atl1c_get_permanent_address(struct atl1c_hw *hw)
|
|
||||||
{
|
|
||||||
u32 i;
|
|
||||||
u32 otp_ctrl_data;
|
|
||||||
u32 twsi_ctrl_data;
|
|
||||||
u16 phy_data;
|
|
||||||
bool raise_vol = false;
|
|
||||||
|
|
||||||
/* MAC-address from BIOS is the 1st priority */
|
|
||||||
if (atl1c_read_current_addr(hw, hw->perm_mac_addr))
|
|
||||||
return 0;
|
|
||||||
|
|
||||||
/* init */
|
|
||||||
AT_READ_REG(hw, REG_OTP_CTRL, &otp_ctrl_data);
|
|
||||||
if (atl1c_check_eeprom_exist(hw)) {
|
|
||||||
if (hw->nic_type == athr_l1c || hw->nic_type == athr_l2c) {
|
|
||||||
/* Enable OTP CLK */
|
|
||||||
if (!(otp_ctrl_data & OTP_CTRL_CLK_EN)) {
|
|
||||||
otp_ctrl_data |= OTP_CTRL_CLK_EN;
|
|
||||||
AT_WRITE_REG(hw, REG_OTP_CTRL, otp_ctrl_data);
|
|
||||||
AT_WRITE_FLUSH(hw);
|
|
||||||
msleep(1);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
/* raise voltage temporally for l2cb */
|
|
||||||
if (hw->nic_type == athr_l2c_b || hw->nic_type == athr_l2c_b2) {
|
|
||||||
atl1c_read_phy_dbg(hw, MIIDBG_ANACTRL, &phy_data);
|
|
||||||
phy_data &= ~ANACTRL_HB_EN;
|
|
||||||
atl1c_write_phy_dbg(hw, MIIDBG_ANACTRL, phy_data);
|
|
||||||
atl1c_read_phy_dbg(hw, MIIDBG_VOLT_CTRL, &phy_data);
|
|
||||||
phy_data |= VOLT_CTRL_SWLOWEST;
|
|
||||||
atl1c_write_phy_dbg(hw, MIIDBG_VOLT_CTRL, phy_data);
|
|
||||||
udelay(20);
|
|
||||||
raise_vol = true;
|
|
||||||
}
|
|
||||||
|
|
||||||
AT_READ_REG(hw, REG_TWSI_CTRL, &twsi_ctrl_data);
|
|
||||||
twsi_ctrl_data |= TWSI_CTRL_SW_LDSTART;
|
|
||||||
AT_WRITE_REG(hw, REG_TWSI_CTRL, twsi_ctrl_data);
|
|
||||||
for (i = 0; i < AT_TWSI_EEPROM_TIMEOUT; i++) {
|
|
||||||
msleep(10);
|
|
||||||
AT_READ_REG(hw, REG_TWSI_CTRL, &twsi_ctrl_data);
|
|
||||||
if ((twsi_ctrl_data & TWSI_CTRL_SW_LDSTART) == 0)
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
if (i >= AT_TWSI_EEPROM_TIMEOUT)
|
|
||||||
return -1;
|
|
||||||
}
|
|
||||||
/* Disable OTP_CLK */
|
|
||||||
if ((hw->nic_type == athr_l1c || hw->nic_type == athr_l2c)) {
|
|
||||||
otp_ctrl_data &= ~OTP_CTRL_CLK_EN;
|
|
||||||
AT_WRITE_REG(hw, REG_OTP_CTRL, otp_ctrl_data);
|
|
||||||
msleep(1);
|
|
||||||
}
|
|
||||||
if (raise_vol) {
|
|
||||||
atl1c_read_phy_dbg(hw, MIIDBG_ANACTRL, &phy_data);
|
|
||||||
phy_data |= ANACTRL_HB_EN;
|
|
||||||
atl1c_write_phy_dbg(hw, MIIDBG_ANACTRL, phy_data);
|
|
||||||
atl1c_read_phy_dbg(hw, MIIDBG_VOLT_CTRL, &phy_data);
|
|
||||||
phy_data &= ~VOLT_CTRL_SWLOWEST;
|
|
||||||
atl1c_write_phy_dbg(hw, MIIDBG_VOLT_CTRL, phy_data);
|
|
||||||
udelay(20);
|
|
||||||
}
|
|
||||||
|
|
||||||
if (atl1c_read_current_addr(hw, hw->perm_mac_addr))
|
|
||||||
return 0;
|
|
||||||
|
|
||||||
return -1;
|
|
||||||
}
|
|
||||||
|
|
||||||
bool atl1c_read_eeprom(struct atl1c_hw *hw, u32 offset, u32 *p_value)
|
|
||||||
{
|
|
||||||
int i;
|
|
||||||
bool ret = false;
|
|
||||||
u32 otp_ctrl_data;
|
|
||||||
u32 control;
|
|
||||||
u32 data;
|
|
||||||
|
|
||||||
if (offset & 3)
|
|
||||||
return ret; /* address do not align */
|
|
||||||
|
|
||||||
AT_READ_REG(hw, REG_OTP_CTRL, &otp_ctrl_data);
|
|
||||||
if (!(otp_ctrl_data & OTP_CTRL_CLK_EN))
|
|
||||||
AT_WRITE_REG(hw, REG_OTP_CTRL,
|
|
||||||
(otp_ctrl_data | OTP_CTRL_CLK_EN));
|
|
||||||
|
|
||||||
AT_WRITE_REG(hw, REG_EEPROM_DATA_LO, 0);
|
|
||||||
control = (offset & EEPROM_CTRL_ADDR_MASK) << EEPROM_CTRL_ADDR_SHIFT;
|
|
||||||
AT_WRITE_REG(hw, REG_EEPROM_CTRL, control);
|
|
||||||
|
|
||||||
for (i = 0; i < 10; i++) {
|
|
||||||
udelay(100);
|
|
||||||
AT_READ_REG(hw, REG_EEPROM_CTRL, &control);
|
|
||||||
if (control & EEPROM_CTRL_RW)
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
if (control & EEPROM_CTRL_RW) {
|
|
||||||
AT_READ_REG(hw, REG_EEPROM_CTRL, &data);
|
|
||||||
AT_READ_REG(hw, REG_EEPROM_DATA_LO, p_value);
|
|
||||||
data = data & 0xFFFF;
|
|
||||||
*p_value = swab32((data << 16) | (*p_value >> 16));
|
|
||||||
ret = true;
|
|
||||||
}
|
|
||||||
if (!(otp_ctrl_data & OTP_CTRL_CLK_EN))
|
|
||||||
AT_WRITE_REG(hw, REG_OTP_CTRL, otp_ctrl_data);
|
|
||||||
|
|
||||||
return ret;
|
|
||||||
}
|
|
||||||
/*
|
|
||||||
* Reads the adapter's MAC address from the EEPROM
|
|
||||||
*
|
|
||||||
* hw - Struct containing variables accessed by shared code
|
|
||||||
*/
|
|
||||||
int atl1c_read_mac_addr(struct atl1c_hw *hw)
|
|
||||||
{
|
|
||||||
int err = 0;
|
|
||||||
|
|
||||||
err = atl1c_get_permanent_address(hw);
|
|
||||||
if (err)
|
|
||||||
eth_random_addr(hw->perm_mac_addr);
|
|
||||||
|
|
||||||
memcpy(hw->mac_addr, hw->perm_mac_addr, sizeof(hw->perm_mac_addr));
|
|
||||||
return err;
|
|
||||||
}
|
|
||||||
|
|
||||||
/*
|
|
||||||
* atl1c_hash_mc_addr
|
|
||||||
* purpose
|
|
||||||
* set hash value for a multicast address
|
|
||||||
* hash calcu processing :
|
|
||||||
* 1. calcu 32bit CRC for multicast address
|
|
||||||
* 2. reverse crc with MSB to LSB
|
|
||||||
*/
|
|
||||||
u32 atl1c_hash_mc_addr(struct atl1c_hw *hw, u8 *mc_addr)
|
|
||||||
{
|
|
||||||
u32 crc32;
|
|
||||||
u32 value = 0;
|
|
||||||
int i;
|
|
||||||
|
|
||||||
crc32 = ether_crc_le(6, mc_addr);
|
|
||||||
for (i = 0; i < 32; i++)
|
|
||||||
value |= (((crc32 >> i) & 1) << (31 - i));
|
|
||||||
|
|
||||||
return value;
|
|
||||||
}
|
|
||||||
|
|
||||||
/*
|
|
||||||
* Sets the bit in the multicast table corresponding to the hash value.
|
|
||||||
* hw - Struct containing variables accessed by shared code
|
|
||||||
* hash_value - Multicast address hash value
|
|
||||||
*/
|
|
||||||
void atl1c_hash_set(struct atl1c_hw *hw, u32 hash_value)
|
|
||||||
{
|
|
||||||
u32 hash_bit, hash_reg;
|
|
||||||
u32 mta;
|
|
||||||
|
|
||||||
/*
|
|
||||||
* The HASH Table is a register array of 2 32-bit registers.
|
|
||||||
* It is treated like an array of 64 bits. We want to set
|
|
||||||
* bit BitArray[hash_value]. So we figure out what register
|
|
||||||
* the bit is in, read it, OR in the new bit, then write
|
|
||||||
* back the new value. The register is determined by the
|
|
||||||
* upper bit of the hash value and the bit within that
|
|
||||||
* register are determined by the lower 5 bits of the value.
|
|
||||||
*/
|
|
||||||
hash_reg = (hash_value >> 31) & 0x1;
|
|
||||||
hash_bit = (hash_value >> 26) & 0x1F;
|
|
||||||
|
|
||||||
mta = AT_READ_REG_ARRAY(hw, REG_RX_HASH_TABLE, hash_reg);
|
|
||||||
|
|
||||||
mta |= (1 << hash_bit);
|
|
||||||
|
|
||||||
AT_WRITE_REG_ARRAY(hw, REG_RX_HASH_TABLE, hash_reg, mta);
|
|
||||||
}
|
|
||||||
|
|
||||||
/*
|
|
||||||
* wait mdio module be idle
|
|
||||||
* return true: idle
|
|
||||||
* false: still busy
|
|
||||||
*/
|
|
||||||
bool atl1c_wait_mdio_idle(struct atl1c_hw *hw)
|
|
||||||
{
|
|
||||||
u32 val;
|
|
||||||
int i;
|
|
||||||
|
|
||||||
for (i = 0; i < MDIO_MAX_AC_TO; i++) {
|
|
||||||
AT_READ_REG(hw, REG_MDIO_CTRL, &val);
|
|
||||||
if (!(val & (MDIO_CTRL_BUSY | MDIO_CTRL_START)))
|
|
||||||
break;
|
|
||||||
udelay(10);
|
|
||||||
}
|
|
||||||
|
|
||||||
return i != MDIO_MAX_AC_TO;
|
|
||||||
}
|
|
||||||
|
|
||||||
void atl1c_stop_phy_polling(struct atl1c_hw *hw)
|
|
||||||
{
|
|
||||||
if (!(hw->ctrl_flags & ATL1C_FPGA_VERSION))
|
|
||||||
return;
|
|
||||||
|
|
||||||
AT_WRITE_REG(hw, REG_MDIO_CTRL, 0);
|
|
||||||
atl1c_wait_mdio_idle(hw);
|
|
||||||
}
|
|
||||||
|
|
||||||
void atl1c_start_phy_polling(struct atl1c_hw *hw, u16 clk_sel)
|
|
||||||
{
|
|
||||||
u32 val;
|
|
||||||
|
|
||||||
if (!(hw->ctrl_flags & ATL1C_FPGA_VERSION))
|
|
||||||
return;
|
|
||||||
|
|
||||||
val = MDIO_CTRL_SPRES_PRMBL |
|
|
||||||
FIELDX(MDIO_CTRL_CLK_SEL, clk_sel) |
|
|
||||||
FIELDX(MDIO_CTRL_REG, 1) |
|
|
||||||
MDIO_CTRL_START |
|
|
||||||
MDIO_CTRL_OP_READ;
|
|
||||||
AT_WRITE_REG(hw, REG_MDIO_CTRL, val);
|
|
||||||
atl1c_wait_mdio_idle(hw);
|
|
||||||
val |= MDIO_CTRL_AP_EN;
|
|
||||||
val &= ~MDIO_CTRL_START;
|
|
||||||
AT_WRITE_REG(hw, REG_MDIO_CTRL, val);
|
|
||||||
udelay(30);
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
/*
|
|
||||||
* atl1c_read_phy_core
|
|
||||||
* core function to read register in PHY via MDIO control regsiter.
|
|
||||||
* ext: extension register (see IEEE 802.3)
|
|
||||||
* dev: device address (see IEEE 802.3 DEVAD, PRTAD is fixed to 0)
|
|
||||||
* reg: reg to read
|
|
||||||
*/
|
|
||||||
int atl1c_read_phy_core(struct atl1c_hw *hw, bool ext, u8 dev,
|
|
||||||
u16 reg, u16 *phy_data)
|
|
||||||
{
|
|
||||||
u32 val;
|
|
||||||
u16 clk_sel = MDIO_CTRL_CLK_25_4;
|
|
||||||
|
|
||||||
atl1c_stop_phy_polling(hw);
|
|
||||||
|
|
||||||
*phy_data = 0;
|
|
||||||
|
|
||||||
/* only l2c_b2 & l1d_2 could use slow clock */
|
|
||||||
if ((hw->nic_type == athr_l2c_b2 || hw->nic_type == athr_l1d_2) &&
|
|
||||||
hw->hibernate)
|
|
||||||
clk_sel = MDIO_CTRL_CLK_25_128;
|
|
||||||
if (ext) {
|
|
||||||
val = FIELDX(MDIO_EXTN_DEVAD, dev) | FIELDX(MDIO_EXTN_REG, reg);
|
|
||||||
AT_WRITE_REG(hw, REG_MDIO_EXTN, val);
|
|
||||||
val = MDIO_CTRL_SPRES_PRMBL |
|
|
||||||
FIELDX(MDIO_CTRL_CLK_SEL, clk_sel) |
|
|
||||||
MDIO_CTRL_START |
|
|
||||||
MDIO_CTRL_MODE_EXT |
|
|
||||||
MDIO_CTRL_OP_READ;
|
|
||||||
} else {
|
|
||||||
val = MDIO_CTRL_SPRES_PRMBL |
|
|
||||||
FIELDX(MDIO_CTRL_CLK_SEL, clk_sel) |
|
|
||||||
FIELDX(MDIO_CTRL_REG, reg) |
|
|
||||||
MDIO_CTRL_START |
|
|
||||||
MDIO_CTRL_OP_READ;
|
|
||||||
}
|
|
||||||
AT_WRITE_REG(hw, REG_MDIO_CTRL, val);
|
|
||||||
|
|
||||||
if (!atl1c_wait_mdio_idle(hw))
|
|
||||||
return -1;
|
|
||||||
|
|
||||||
AT_READ_REG(hw, REG_MDIO_CTRL, &val);
|
|
||||||
*phy_data = (u16)FIELD_GETX(val, MDIO_CTRL_DATA);
|
|
||||||
|
|
||||||
atl1c_start_phy_polling(hw, clk_sel);
|
|
||||||
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
/*
|
|
||||||
* atl1c_write_phy_core
|
|
||||||
* core function to write to register in PHY via MDIO control register.
|
|
||||||
* ext: extension register (see IEEE 802.3)
|
|
||||||
* dev: device address (see IEEE 802.3 DEVAD, PRTAD is fixed to 0)
|
|
||||||
* reg: reg to write
|
|
||||||
*/
|
|
||||||
int atl1c_write_phy_core(struct atl1c_hw *hw, bool ext, u8 dev,
|
|
||||||
u16 reg, u16 phy_data)
|
|
||||||
{
|
|
||||||
u32 val;
|
|
||||||
u16 clk_sel = MDIO_CTRL_CLK_25_4;
|
|
||||||
|
|
||||||
atl1c_stop_phy_polling(hw);
|
|
||||||
|
|
||||||
|
|
||||||
/* only l2c_b2 & l1d_2 could use slow clock */
|
|
||||||
if ((hw->nic_type == athr_l2c_b2 || hw->nic_type == athr_l1d_2) &&
|
|
||||||
hw->hibernate)
|
|
||||||
clk_sel = MDIO_CTRL_CLK_25_128;
|
|
||||||
|
|
||||||
if (ext) {
|
|
||||||
val = FIELDX(MDIO_EXTN_DEVAD, dev) | FIELDX(MDIO_EXTN_REG, reg);
|
|
||||||
AT_WRITE_REG(hw, REG_MDIO_EXTN, val);
|
|
||||||
val = MDIO_CTRL_SPRES_PRMBL |
|
|
||||||
FIELDX(MDIO_CTRL_CLK_SEL, clk_sel) |
|
|
||||||
FIELDX(MDIO_CTRL_DATA, phy_data) |
|
|
||||||
MDIO_CTRL_START |
|
|
||||||
MDIO_CTRL_MODE_EXT;
|
|
||||||
} else {
|
|
||||||
val = MDIO_CTRL_SPRES_PRMBL |
|
|
||||||
FIELDX(MDIO_CTRL_CLK_SEL, clk_sel) |
|
|
||||||
FIELDX(MDIO_CTRL_DATA, phy_data) |
|
|
||||||
FIELDX(MDIO_CTRL_REG, reg) |
|
|
||||||
MDIO_CTRL_START;
|
|
||||||
}
|
|
||||||
AT_WRITE_REG(hw, REG_MDIO_CTRL, val);
|
|
||||||
|
|
||||||
if (!atl1c_wait_mdio_idle(hw))
|
|
||||||
return -1;
|
|
||||||
|
|
||||||
atl1c_start_phy_polling(hw, clk_sel);
|
|
||||||
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
/*
|
|
||||||
* Reads the value from a PHY register
|
|
||||||
* hw - Struct containing variables accessed by shared code
|
|
||||||
* reg_addr - address of the PHY register to read
|
|
||||||
*/
|
|
||||||
int atl1c_read_phy_reg(struct atl1c_hw *hw, u16 reg_addr, u16 *phy_data)
|
|
||||||
{
|
|
||||||
return atl1c_read_phy_core(hw, false, 0, reg_addr, phy_data);
|
|
||||||
}
|
|
||||||
|
|
||||||
/*
|
|
||||||
* Writes a value to a PHY register
|
|
||||||
* hw - Struct containing variables accessed by shared code
|
|
||||||
* reg_addr - address of the PHY register to write
|
|
||||||
* data - data to write to the PHY
|
|
||||||
*/
|
|
||||||
int atl1c_write_phy_reg(struct atl1c_hw *hw, u32 reg_addr, u16 phy_data)
|
|
||||||
{
|
|
||||||
return atl1c_write_phy_core(hw, false, 0, reg_addr, phy_data);
|
|
||||||
}
|
|
||||||
|
|
||||||
/* read from PHY extension register */
|
|
||||||
int atl1c_read_phy_ext(struct atl1c_hw *hw, u8 dev_addr,
|
|
||||||
u16 reg_addr, u16 *phy_data)
|
|
||||||
{
|
|
||||||
return atl1c_read_phy_core(hw, true, dev_addr, reg_addr, phy_data);
|
|
||||||
}
|
|
||||||
|
|
||||||
/* write to PHY extension register */
|
|
||||||
int atl1c_write_phy_ext(struct atl1c_hw *hw, u8 dev_addr,
|
|
||||||
u16 reg_addr, u16 phy_data)
|
|
||||||
{
|
|
||||||
return atl1c_write_phy_core(hw, true, dev_addr, reg_addr, phy_data);
|
|
||||||
}
|
|
||||||
|
|
||||||
int atl1c_read_phy_dbg(struct atl1c_hw *hw, u16 reg_addr, u16 *phy_data)
|
|
||||||
{
|
|
||||||
int err;
|
|
||||||
|
|
||||||
err = atl1c_write_phy_reg(hw, MII_DBG_ADDR, reg_addr);
|
|
||||||
if (unlikely(err))
|
|
||||||
return err;
|
|
||||||
else
|
|
||||||
err = atl1c_read_phy_reg(hw, MII_DBG_DATA, phy_data);
|
|
||||||
|
|
||||||
return err;
|
|
||||||
}
|
|
||||||
|
|
||||||
int atl1c_write_phy_dbg(struct atl1c_hw *hw, u16 reg_addr, u16 phy_data)
|
|
||||||
{
|
|
||||||
int err;
|
|
||||||
|
|
||||||
err = atl1c_write_phy_reg(hw, MII_DBG_ADDR, reg_addr);
|
|
||||||
if (unlikely(err))
|
|
||||||
return err;
|
|
||||||
else
|
|
||||||
err = atl1c_write_phy_reg(hw, MII_DBG_DATA, phy_data);
|
|
||||||
|
|
||||||
return err;
|
|
||||||
}
|
|
||||||
|
|
||||||
/*
|
|
||||||
* Configures PHY autoneg and flow control advertisement settings
|
|
||||||
*
|
|
||||||
* hw - Struct containing variables accessed by shared code
|
|
||||||
*/
|
|
||||||
static int atl1c_phy_setup_adv(struct atl1c_hw *hw)
|
|
||||||
{
|
|
||||||
u16 mii_adv_data = ADVERTISE_DEFAULT_CAP & ~ADVERTISE_ALL;
|
|
||||||
u16 mii_giga_ctrl_data = GIGA_CR_1000T_DEFAULT_CAP &
|
|
||||||
~GIGA_CR_1000T_SPEED_MASK;
|
|
||||||
|
|
||||||
if (hw->autoneg_advertised & ADVERTISED_10baseT_Half)
|
|
||||||
mii_adv_data |= ADVERTISE_10HALF;
|
|
||||||
if (hw->autoneg_advertised & ADVERTISED_10baseT_Full)
|
|
||||||
mii_adv_data |= ADVERTISE_10FULL;
|
|
||||||
if (hw->autoneg_advertised & ADVERTISED_100baseT_Half)
|
|
||||||
mii_adv_data |= ADVERTISE_100HALF;
|
|
||||||
if (hw->autoneg_advertised & ADVERTISED_100baseT_Full)
|
|
||||||
mii_adv_data |= ADVERTISE_100FULL;
|
|
||||||
|
|
||||||
if (hw->autoneg_advertised & ADVERTISED_Autoneg)
|
|
||||||
mii_adv_data |= ADVERTISE_10HALF | ADVERTISE_10FULL |
|
|
||||||
ADVERTISE_100HALF | ADVERTISE_100FULL;
|
|
||||||
|
|
||||||
if (hw->link_cap_flags & ATL1C_LINK_CAP_1000M) {
|
|
||||||
if (hw->autoneg_advertised & ADVERTISED_1000baseT_Half)
|
|
||||||
mii_giga_ctrl_data |= ADVERTISE_1000HALF;
|
|
||||||
if (hw->autoneg_advertised & ADVERTISED_1000baseT_Full)
|
|
||||||
mii_giga_ctrl_data |= ADVERTISE_1000FULL;
|
|
||||||
if (hw->autoneg_advertised & ADVERTISED_Autoneg)
|
|
||||||
mii_giga_ctrl_data |= ADVERTISE_1000HALF |
|
|
||||||
ADVERTISE_1000FULL;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (atl1c_write_phy_reg(hw, MII_ADVERTISE, mii_adv_data) != 0 ||
|
|
||||||
atl1c_write_phy_reg(hw, MII_CTRL1000, mii_giga_ctrl_data) != 0)
|
|
||||||
return -1;
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
void atl1c_phy_disable(struct atl1c_hw *hw)
|
|
||||||
{
|
|
||||||
atl1c_power_saving(hw, 0);
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
int atl1c_phy_reset(struct atl1c_hw *hw)
|
|
||||||
{
|
|
||||||
struct atl1c_adapter *adapter = hw->adapter;
|
|
||||||
struct pci_dev *pdev = adapter->pdev;
|
|
||||||
u16 phy_data;
|
|
||||||
u32 phy_ctrl_data, lpi_ctrl;
|
|
||||||
int err;
|
|
||||||
|
|
||||||
/* reset PHY core */
|
|
||||||
AT_READ_REG(hw, REG_GPHY_CTRL, &phy_ctrl_data);
|
|
||||||
phy_ctrl_data &= ~(GPHY_CTRL_EXT_RESET | GPHY_CTRL_PHY_IDDQ |
|
|
||||||
GPHY_CTRL_GATE_25M_EN | GPHY_CTRL_PWDOWN_HW | GPHY_CTRL_CLS);
|
|
||||||
phy_ctrl_data |= GPHY_CTRL_SEL_ANA_RST;
|
|
||||||
if (!(hw->ctrl_flags & ATL1C_HIB_DISABLE))
|
|
||||||
phy_ctrl_data |= (GPHY_CTRL_HIB_EN | GPHY_CTRL_HIB_PULSE);
|
|
||||||
else
|
|
||||||
phy_ctrl_data &= ~(GPHY_CTRL_HIB_EN | GPHY_CTRL_HIB_PULSE);
|
|
||||||
AT_WRITE_REG(hw, REG_GPHY_CTRL, phy_ctrl_data);
|
|
||||||
AT_WRITE_FLUSH(hw);
|
|
||||||
udelay(10);
|
|
||||||
AT_WRITE_REG(hw, REG_GPHY_CTRL, phy_ctrl_data | GPHY_CTRL_EXT_RESET);
|
|
||||||
AT_WRITE_FLUSH(hw);
|
|
||||||
udelay(10 * GPHY_CTRL_EXT_RST_TO); /* delay 800us */
|
|
||||||
|
|
||||||
/* switch clock */
|
|
||||||
if (hw->nic_type == athr_l2c_b) {
|
|
||||||
atl1c_read_phy_dbg(hw, MIIDBG_CFGLPSPD, &phy_data);
|
|
||||||
atl1c_write_phy_dbg(hw, MIIDBG_CFGLPSPD,
|
|
||||||
phy_data & ~CFGLPSPD_RSTCNT_CLK125SW);
|
|
||||||
}
|
|
||||||
|
|
||||||
/* tx-half amplitude issue fix */
|
|
||||||
if (hw->nic_type == athr_l2c_b || hw->nic_type == athr_l2c_b2) {
|
|
||||||
atl1c_read_phy_dbg(hw, MIIDBG_CABLE1TH_DET, &phy_data);
|
|
||||||
phy_data |= CABLE1TH_DET_EN;
|
|
||||||
atl1c_write_phy_dbg(hw, MIIDBG_CABLE1TH_DET, phy_data);
|
|
||||||
}
|
|
||||||
|
|
||||||
/* clear bit3 of dbgport 3B to lower voltage */
|
|
||||||
if (!(hw->ctrl_flags & ATL1C_HIB_DISABLE)) {
|
|
||||||
if (hw->nic_type == athr_l2c_b || hw->nic_type == athr_l2c_b2) {
|
|
||||||
atl1c_read_phy_dbg(hw, MIIDBG_VOLT_CTRL, &phy_data);
|
|
||||||
phy_data &= ~VOLT_CTRL_SWLOWEST;
|
|
||||||
atl1c_write_phy_dbg(hw, MIIDBG_VOLT_CTRL, phy_data);
|
|
||||||
}
|
|
||||||
/* power saving config */
|
|
||||||
phy_data =
|
|
||||||
hw->nic_type == athr_l1d || hw->nic_type == athr_l1d_2 ?
|
|
||||||
L1D_LEGCYPS_DEF : L1C_LEGCYPS_DEF;
|
|
||||||
atl1c_write_phy_dbg(hw, MIIDBG_LEGCYPS, phy_data);
|
|
||||||
/* hib */
|
|
||||||
atl1c_write_phy_dbg(hw, MIIDBG_SYSMODCTRL,
|
|
||||||
SYSMODCTRL_IECHOADJ_DEF);
|
|
||||||
} else {
|
|
||||||
/* disable pws */
|
|
||||||
atl1c_read_phy_dbg(hw, MIIDBG_LEGCYPS, &phy_data);
|
|
||||||
atl1c_write_phy_dbg(hw, MIIDBG_LEGCYPS,
|
|
||||||
phy_data & ~LEGCYPS_EN);
|
|
||||||
/* disable hibernate */
|
|
||||||
atl1c_read_phy_dbg(hw, MIIDBG_HIBNEG, &phy_data);
|
|
||||||
atl1c_write_phy_dbg(hw, MIIDBG_HIBNEG,
|
|
||||||
phy_data & HIBNEG_PSHIB_EN);
|
|
||||||
}
|
|
||||||
/* disable AZ(EEE) by default */
|
|
||||||
if (hw->nic_type == athr_l1d || hw->nic_type == athr_l1d_2 ||
|
|
||||||
hw->nic_type == athr_l2c_b2) {
|
|
||||||
AT_READ_REG(hw, REG_LPI_CTRL, &lpi_ctrl);
|
|
||||||
AT_WRITE_REG(hw, REG_LPI_CTRL, lpi_ctrl & ~LPI_CTRL_EN);
|
|
||||||
atl1c_write_phy_ext(hw, MIIEXT_ANEG, MIIEXT_LOCAL_EEEADV, 0);
|
|
||||||
atl1c_write_phy_ext(hw, MIIEXT_PCS, MIIEXT_CLDCTRL3,
|
|
||||||
L2CB_CLDCTRL3);
|
|
||||||
}
|
|
||||||
|
|
||||||
/* other debug port to set */
|
|
||||||
atl1c_write_phy_dbg(hw, MIIDBG_ANACTRL, ANACTRL_DEF);
|
|
||||||
atl1c_write_phy_dbg(hw, MIIDBG_SRDSYSMOD, SRDSYSMOD_DEF);
|
|
||||||
atl1c_write_phy_dbg(hw, MIIDBG_TST10BTCFG, TST10BTCFG_DEF);
|
|
||||||
/* UNH-IOL test issue, set bit7 */
|
|
||||||
atl1c_write_phy_dbg(hw, MIIDBG_TST100BTCFG,
|
|
||||||
TST100BTCFG_DEF | TST100BTCFG_LITCH_EN);
|
|
||||||
|
|
||||||
/* set phy interrupt mask */
|
|
||||||
phy_data = IER_LINK_UP | IER_LINK_DOWN;
|
|
||||||
err = atl1c_write_phy_reg(hw, MII_IER, phy_data);
|
|
||||||
if (err) {
|
|
||||||
if (netif_msg_hw(adapter))
|
|
||||||
dev_err(&pdev->dev,
|
|
||||||
"Error enable PHY linkChange Interrupt\n");
|
|
||||||
return err;
|
|
||||||
}
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
int atl1c_phy_init(struct atl1c_hw *hw)
|
|
||||||
{
|
|
||||||
struct atl1c_adapter *adapter = hw->adapter;
|
|
||||||
struct pci_dev *pdev = adapter->pdev;
|
|
||||||
int ret_val;
|
|
||||||
u16 mii_bmcr_data = BMCR_RESET;
|
|
||||||
|
|
||||||
if ((atl1c_read_phy_reg(hw, MII_PHYSID1, &hw->phy_id1) != 0) ||
|
|
||||||
(atl1c_read_phy_reg(hw, MII_PHYSID2, &hw->phy_id2) != 0)) {
|
|
||||||
dev_err(&pdev->dev, "Error get phy ID\n");
|
|
||||||
return -1;
|
|
||||||
}
|
|
||||||
switch (hw->media_type) {
|
|
||||||
case MEDIA_TYPE_AUTO_SENSOR:
|
|
||||||
ret_val = atl1c_phy_setup_adv(hw);
|
|
||||||
if (ret_val) {
|
|
||||||
if (netif_msg_link(adapter))
|
|
||||||
dev_err(&pdev->dev,
|
|
||||||
"Error Setting up Auto-Negotiation\n");
|
|
||||||
return ret_val;
|
|
||||||
}
|
|
||||||
mii_bmcr_data |= BMCR_ANENABLE | BMCR_ANRESTART;
|
|
||||||
break;
|
|
||||||
case MEDIA_TYPE_100M_FULL:
|
|
||||||
mii_bmcr_data |= BMCR_SPEED100 | BMCR_FULLDPLX;
|
|
||||||
break;
|
|
||||||
case MEDIA_TYPE_100M_HALF:
|
|
||||||
mii_bmcr_data |= BMCR_SPEED100;
|
|
||||||
break;
|
|
||||||
case MEDIA_TYPE_10M_FULL:
|
|
||||||
mii_bmcr_data |= BMCR_FULLDPLX;
|
|
||||||
break;
|
|
||||||
case MEDIA_TYPE_10M_HALF:
|
|
||||||
break;
|
|
||||||
default:
|
|
||||||
if (netif_msg_link(adapter))
|
|
||||||
dev_err(&pdev->dev, "Wrong Media type %d\n",
|
|
||||||
hw->media_type);
|
|
||||||
return -1;
|
|
||||||
}
|
|
||||||
|
|
||||||
ret_val = atl1c_write_phy_reg(hw, MII_BMCR, mii_bmcr_data);
|
|
||||||
if (ret_val)
|
|
||||||
return ret_val;
|
|
||||||
hw->phy_configured = true;
|
|
||||||
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
/*
|
|
||||||
* Detects the current speed and duplex settings of the hardware.
|
|
||||||
*
|
|
||||||
* hw - Struct containing variables accessed by shared code
|
|
||||||
* speed - Speed of the connection
|
|
||||||
* duplex - Duplex setting of the connection
|
|
||||||
*/
|
|
||||||
int atl1c_get_speed_and_duplex(struct atl1c_hw *hw, u16 *speed, u16 *duplex)
|
|
||||||
{
|
|
||||||
int err;
|
|
||||||
u16 phy_data;
|
|
||||||
|
|
||||||
/* Read PHY Specific Status Register (17) */
|
|
||||||
err = atl1c_read_phy_reg(hw, MII_GIGA_PSSR, &phy_data);
|
|
||||||
if (err)
|
|
||||||
return err;
|
|
||||||
|
|
||||||
if (!(phy_data & GIGA_PSSR_SPD_DPLX_RESOLVED))
|
|
||||||
return -1;
|
|
||||||
|
|
||||||
switch (phy_data & GIGA_PSSR_SPEED) {
|
|
||||||
case GIGA_PSSR_1000MBS:
|
|
||||||
*speed = SPEED_1000;
|
|
||||||
break;
|
|
||||||
case GIGA_PSSR_100MBS:
|
|
||||||
*speed = SPEED_100;
|
|
||||||
break;
|
|
||||||
case GIGA_PSSR_10MBS:
|
|
||||||
*speed = SPEED_10;
|
|
||||||
break;
|
|
||||||
default:
|
|
||||||
return -1;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (phy_data & GIGA_PSSR_DPLX)
|
|
||||||
*duplex = FULL_DUPLEX;
|
|
||||||
else
|
|
||||||
*duplex = HALF_DUPLEX;
|
|
||||||
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
/* select one link mode to get lower power consumption */
|
|
||||||
int atl1c_phy_to_ps_link(struct atl1c_hw *hw)
|
|
||||||
{
|
|
||||||
struct atl1c_adapter *adapter = hw->adapter;
|
|
||||||
struct pci_dev *pdev = adapter->pdev;
|
|
||||||
int ret = 0;
|
|
||||||
u16 autoneg_advertised = ADVERTISED_10baseT_Half;
|
|
||||||
u16 save_autoneg_advertised;
|
|
||||||
u16 phy_data;
|
|
||||||
u16 mii_lpa_data;
|
|
||||||
u16 speed = SPEED_0;
|
|
||||||
u16 duplex = FULL_DUPLEX;
|
|
||||||
int i;
|
|
||||||
|
|
||||||
atl1c_read_phy_reg(hw, MII_BMSR, &phy_data);
|
|
||||||
atl1c_read_phy_reg(hw, MII_BMSR, &phy_data);
|
|
||||||
if (phy_data & BMSR_LSTATUS) {
|
|
||||||
atl1c_read_phy_reg(hw, MII_LPA, &mii_lpa_data);
|
|
||||||
if (mii_lpa_data & LPA_10FULL)
|
|
||||||
autoneg_advertised = ADVERTISED_10baseT_Full;
|
|
||||||
else if (mii_lpa_data & LPA_10HALF)
|
|
||||||
autoneg_advertised = ADVERTISED_10baseT_Half;
|
|
||||||
else if (mii_lpa_data & LPA_100HALF)
|
|
||||||
autoneg_advertised = ADVERTISED_100baseT_Half;
|
|
||||||
else if (mii_lpa_data & LPA_100FULL)
|
|
||||||
autoneg_advertised = ADVERTISED_100baseT_Full;
|
|
||||||
|
|
||||||
save_autoneg_advertised = hw->autoneg_advertised;
|
|
||||||
hw->phy_configured = false;
|
|
||||||
hw->autoneg_advertised = autoneg_advertised;
|
|
||||||
if (atl1c_restart_autoneg(hw) != 0) {
|
|
||||||
dev_dbg(&pdev->dev, "phy autoneg failed\n");
|
|
||||||
ret = -1;
|
|
||||||
}
|
|
||||||
hw->autoneg_advertised = save_autoneg_advertised;
|
|
||||||
|
|
||||||
if (mii_lpa_data) {
|
|
||||||
for (i = 0; i < AT_SUSPEND_LINK_TIMEOUT; i++) {
|
|
||||||
mdelay(100);
|
|
||||||
atl1c_read_phy_reg(hw, MII_BMSR, &phy_data);
|
|
||||||
atl1c_read_phy_reg(hw, MII_BMSR, &phy_data);
|
|
||||||
if (phy_data & BMSR_LSTATUS) {
|
|
||||||
if (atl1c_get_speed_and_duplex(hw, &speed,
|
|
||||||
&duplex) != 0)
|
|
||||||
dev_dbg(&pdev->dev,
|
|
||||||
"get speed and duplex failed\n");
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
speed = SPEED_10;
|
|
||||||
duplex = HALF_DUPLEX;
|
|
||||||
}
|
|
||||||
adapter->link_speed = speed;
|
|
||||||
adapter->link_duplex = duplex;
|
|
||||||
|
|
||||||
return ret;
|
|
||||||
}
|
|
||||||
|
|
||||||
int atl1c_restart_autoneg(struct atl1c_hw *hw)
|
|
||||||
{
|
|
||||||
int err = 0;
|
|
||||||
u16 mii_bmcr_data = BMCR_RESET;
|
|
||||||
|
|
||||||
err = atl1c_phy_setup_adv(hw);
|
|
||||||
if (err)
|
|
||||||
return err;
|
|
||||||
mii_bmcr_data |= BMCR_ANENABLE | BMCR_ANRESTART;
|
|
||||||
|
|
||||||
return atl1c_write_phy_reg(hw, MII_BMCR, mii_bmcr_data);
|
|
||||||
}
|
|
||||||
|
|
||||||
int atl1c_power_saving(struct atl1c_hw *hw, u32 wufc)
|
|
||||||
{
|
|
||||||
struct atl1c_adapter *adapter = hw->adapter;
|
|
||||||
struct pci_dev *pdev = adapter->pdev;
|
|
||||||
u32 master_ctrl, mac_ctrl, phy_ctrl;
|
|
||||||
u32 wol_ctrl, speed;
|
|
||||||
u16 phy_data;
|
|
||||||
|
|
||||||
wol_ctrl = 0;
|
|
||||||
speed = adapter->link_speed == SPEED_1000 ?
|
|
||||||
MAC_CTRL_SPEED_1000 : MAC_CTRL_SPEED_10_100;
|
|
||||||
|
|
||||||
AT_READ_REG(hw, REG_MASTER_CTRL, &master_ctrl);
|
|
||||||
AT_READ_REG(hw, REG_MAC_CTRL, &mac_ctrl);
|
|
||||||
AT_READ_REG(hw, REG_GPHY_CTRL, &phy_ctrl);
|
|
||||||
|
|
||||||
master_ctrl &= ~MASTER_CTRL_CLK_SEL_DIS;
|
|
||||||
mac_ctrl = FIELD_SETX(mac_ctrl, MAC_CTRL_SPEED, speed);
|
|
||||||
mac_ctrl &= ~(MAC_CTRL_DUPLX | MAC_CTRL_RX_EN | MAC_CTRL_TX_EN);
|
|
||||||
if (adapter->link_duplex == FULL_DUPLEX)
|
|
||||||
mac_ctrl |= MAC_CTRL_DUPLX;
|
|
||||||
phy_ctrl &= ~(GPHY_CTRL_EXT_RESET | GPHY_CTRL_CLS);
|
|
||||||
phy_ctrl |= GPHY_CTRL_SEL_ANA_RST | GPHY_CTRL_HIB_PULSE |
|
|
||||||
GPHY_CTRL_HIB_EN;
|
|
||||||
if (!wufc) { /* without WoL */
|
|
||||||
master_ctrl |= MASTER_CTRL_CLK_SEL_DIS;
|
|
||||||
phy_ctrl |= GPHY_CTRL_PHY_IDDQ | GPHY_CTRL_PWDOWN_HW;
|
|
||||||
AT_WRITE_REG(hw, REG_MASTER_CTRL, master_ctrl);
|
|
||||||
AT_WRITE_REG(hw, REG_MAC_CTRL, mac_ctrl);
|
|
||||||
AT_WRITE_REG(hw, REG_GPHY_CTRL, phy_ctrl);
|
|
||||||
AT_WRITE_REG(hw, REG_WOL_CTRL, 0);
|
|
||||||
hw->phy_configured = false; /* re-init PHY when resume */
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
phy_ctrl |= GPHY_CTRL_EXT_RESET;
|
|
||||||
if (wufc & AT_WUFC_MAG) {
|
|
||||||
mac_ctrl |= MAC_CTRL_RX_EN | MAC_CTRL_BC_EN;
|
|
||||||
wol_ctrl |= WOL_MAGIC_EN | WOL_MAGIC_PME_EN;
|
|
||||||
if (hw->nic_type == athr_l2c_b && hw->revision_id == L2CB_V11)
|
|
||||||
wol_ctrl |= WOL_PATTERN_EN | WOL_PATTERN_PME_EN;
|
|
||||||
}
|
|
||||||
if (wufc & AT_WUFC_LNKC) {
|
|
||||||
wol_ctrl |= WOL_LINK_CHG_EN | WOL_LINK_CHG_PME_EN;
|
|
||||||
if (atl1c_write_phy_reg(hw, MII_IER, IER_LINK_UP) != 0) {
|
|
||||||
dev_dbg(&pdev->dev, "%s: write phy MII_IER failed.\n",
|
|
||||||
atl1c_driver_name);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
/* clear PHY interrupt */
|
|
||||||
atl1c_read_phy_reg(hw, MII_ISR, &phy_data);
|
|
||||||
|
|
||||||
dev_dbg(&pdev->dev, "%s: suspend MAC=%x,MASTER=%x,PHY=0x%x,WOL=%x\n",
|
|
||||||
atl1c_driver_name, mac_ctrl, master_ctrl, phy_ctrl, wol_ctrl);
|
|
||||||
AT_WRITE_REG(hw, REG_MASTER_CTRL, master_ctrl);
|
|
||||||
AT_WRITE_REG(hw, REG_MAC_CTRL, mac_ctrl);
|
|
||||||
AT_WRITE_REG(hw, REG_GPHY_CTRL, phy_ctrl);
|
|
||||||
AT_WRITE_REG(hw, REG_WOL_CTRL, wol_ctrl);
|
|
||||||
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
/* configure phy after Link change Event */
|
|
||||||
void atl1c_post_phy_linkchg(struct atl1c_hw *hw, u16 link_speed)
|
|
||||||
{
|
|
||||||
u16 phy_val;
|
|
||||||
bool adj_thresh = false;
|
|
||||||
|
|
||||||
if (hw->nic_type == athr_l2c_b || hw->nic_type == athr_l2c_b2 ||
|
|
||||||
hw->nic_type == athr_l1d || hw->nic_type == athr_l1d_2)
|
|
||||||
adj_thresh = true;
|
|
||||||
|
|
||||||
if (link_speed != SPEED_0) { /* link up */
|
|
||||||
/* az with brcm, half-amp */
|
|
||||||
if (hw->nic_type == athr_l1d_2) {
|
|
||||||
atl1c_read_phy_ext(hw, MIIEXT_PCS, MIIEXT_CLDCTRL6,
|
|
||||||
&phy_val);
|
|
||||||
phy_val = FIELD_GETX(phy_val, CLDCTRL6_CAB_LEN);
|
|
||||||
phy_val = phy_val > CLDCTRL6_CAB_LEN_SHORT ?
|
|
||||||
AZ_ANADECT_LONG : AZ_ANADECT_DEF;
|
|
||||||
atl1c_write_phy_dbg(hw, MIIDBG_AZ_ANADECT, phy_val);
|
|
||||||
}
|
|
||||||
/* threshold adjust */
|
|
||||||
if (adj_thresh && link_speed == SPEED_100 && hw->msi_lnkpatch) {
|
|
||||||
atl1c_write_phy_dbg(hw, MIIDBG_MSE16DB, L1D_MSE16DB_UP);
|
|
||||||
atl1c_write_phy_dbg(hw, MIIDBG_SYSMODCTRL,
|
|
||||||
L1D_SYSMODCTRL_IECHOADJ_DEF);
|
|
||||||
}
|
|
||||||
} else { /* link down */
|
|
||||||
if (adj_thresh && hw->msi_lnkpatch) {
|
|
||||||
atl1c_write_phy_dbg(hw, MIIDBG_SYSMODCTRL,
|
|
||||||
SYSMODCTRL_IECHOADJ_DEF);
|
|
||||||
atl1c_write_phy_dbg(hw, MIIDBG_MSE16DB,
|
|
||||||
L1D_MSE16DB_DOWN);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
@ -1,4 +0,0 @@
|
|||||||
if [ "${1}" = "rd" ]; then
|
|
||||||
echo "Installing module for Atheros L1E Gigabit Ethernet adapter"
|
|
||||||
${INSMOD} "/modules/atl1e.ko" ${PARAMS}
|
|
||||||
fi
|
|
@ -1,28 +0,0 @@
|
|||||||
version: 1
|
|
||||||
name: atl1e
|
|
||||||
description: "Driver for Atheros L1E Gigabit Ethernet adapters"
|
|
||||||
available-for:
|
|
||||||
bromolow-3.10.108:
|
|
||||||
install-script: &script "install.sh"
|
|
||||||
modules: true
|
|
||||||
apollolake-4.4.180:
|
|
||||||
install-script: *script
|
|
||||||
modules: true
|
|
||||||
broadwell-4.4.180:
|
|
||||||
install-script: *script
|
|
||||||
modules: true
|
|
||||||
broadwellnk-4.4.180:
|
|
||||||
install-script: *script
|
|
||||||
modules: true
|
|
||||||
denverton-4.4.180:
|
|
||||||
install-script: *script
|
|
||||||
modules: true
|
|
||||||
geminilake-4.4.180:
|
|
||||||
install-script: *script
|
|
||||||
modules: true
|
|
||||||
v1000-4.4.180:
|
|
||||||
install-script: *script
|
|
||||||
modules: true
|
|
||||||
purley-4.4.180:
|
|
||||||
install-script: *script
|
|
||||||
modules: true
|
|
@ -1,2 +0,0 @@
|
|||||||
obj-m += atl1e.o
|
|
||||||
atl1e-objs += atl1e_main.o atl1e_hw.o atl1e_ethtool.o atl1e_param.o
|
|
Some files were not shown because too many files have changed in this diff Show More
Loading…
Reference in New Issue
Block a user