mirror of
https://github.com/AuxXxilium/arc.git
synced 2024-11-23 19:39:53 +07:00
init: arc
Signed-off-by: AuxXxilium <info@auxxxilium.tech>
This commit is contained in:
parent
bad838d8b4
commit
3dcc744dcd
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 Bug Report
|
||||
title: ''
|
||||
labels: bug
|
||||
assignees: ''
|
||||
|
||||
---
|
||||
|
||||
**Describe the Bug**
|
||||
First read Wiki to find a Solution. If you can't find a Solution, provide a clear and concise description of what the bug is.
|
||||
|
||||
**Screenshots**
|
||||
Actual Screenshot from Systeminfo (Config Mode - Sysinfo) or use new "Full Sysinfo - Upload" Function provide the Code!!! <- IMPORTANT
|
||||
|
||||
**Hardware**
|
||||
Add your Hardware Informations here. CPU / Board / Networkcontroller / Storagecontroller / ...
|
8
.github/ISSUE_TEMPLATE/custom_build.md
vendored
Normal file
8
.github/ISSUE_TEMPLATE/custom_build.md
vendored
Normal file
@ -0,0 +1,8 @@
|
||||
---
|
||||
name: Custom Build
|
||||
about: Create a Customized Build
|
||||
title: ''
|
||||
labels: 'custom'
|
||||
assignees: ''
|
||||
|
||||
---
|
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.
|
148
.github/workflows/autobuild.yml
vendored
Normal file
148
.github/workflows/autobuild.yml
vendored
Normal file
@ -0,0 +1,148 @@
|
||||
#
|
||||
# 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
|
||||
|
||||
- name: Free Disk Space (Ubuntu)
|
||||
uses: jlumbroso/free-disk-space@main
|
||||
with:
|
||||
swap-storage: false
|
||||
|
||||
# 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"
|
||||
|
||||
sudo apt update
|
||||
sudo apt install -y jq gawk cpio gettext libelf-dev qemu-utils busybox dialog curl sed
|
||||
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/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"
|
||||
getAddons "files/p3/addons"
|
||||
getModules "files/p3/modules"
|
||||
getConfigs "files/p3/configs"
|
||||
getPatches "files/p3/patches"
|
||||
getTheme "files/p1/boot/grub"
|
||||
|
||||
echo "OK"
|
||||
|
||||
# Build incremental
|
||||
- name: Build image
|
||||
run: |
|
||||
. scripts/func.sh
|
||||
|
||||
echo "Create Arc Image"
|
||||
IMAGE_FILE="arc.img"
|
||||
gzip -dc "files/initrd/opt/arc/grub.img.gz" >"${IMAGE_FILE}"
|
||||
fdisk -l "${IMAGE_FILE}"
|
||||
|
||||
LOOPX=$(sudo losetup -f)
|
||||
sudo losetup -P "${LOOPX}" "${IMAGE_FILE}"
|
||||
|
||||
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.08.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
|
||||
if: success() && env.VERSION == ''
|
||||
uses: actions/upload-artifact@v4
|
||||
with:
|
||||
name: Images
|
||||
path: |
|
||||
update.zip
|
||||
retention-days: 5
|
||||
|
||||
# Publish a release if is a tag
|
||||
- name: Release
|
||||
if: success() && env.VERSION != ''
|
||||
uses: ncipollo/release-action@v1
|
||||
with:
|
||||
tag: ${{ env.VERSION }}
|
||||
prerelease: true
|
||||
allowUpdates: true
|
||||
artifacts: |
|
||||
arc-${{ env.VERSION }}.img.zip
|
||||
arc-${{ env.VERSION }}.vmdk-dyn.zip
|
||||
arc-${{ env.VERSION }}.vmdk-flat.zip
|
||||
update.zip
|
237
.github/workflows/build.yml
vendored
Normal file
237
.github/workflows/build.yml
vendored
Normal file
@ -0,0 +1,237 @@
|
||||
#
|
||||
# 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
|
||||
notice:
|
||||
description: "Notice here"
|
||||
required: false
|
||||
type: string
|
||||
prerelease:
|
||||
description: "pre release"
|
||||
default: false
|
||||
type: boolean
|
||||
clean:
|
||||
description: "clean"
|
||||
default: false
|
||||
type: boolean
|
||||
addons_version:
|
||||
description: "addons version"
|
||||
default: ""
|
||||
type: string
|
||||
configs_version:
|
||||
description: "configs version"
|
||||
default: ""
|
||||
type: string
|
||||
modules_version:
|
||||
description: "modules version"
|
||||
default: ""
|
||||
type: string
|
||||
lkm_version:
|
||||
description: "lkm version"
|
||||
default: ""
|
||||
type: string
|
||||
patches_version:
|
||||
description: "patches version"
|
||||
default: ""
|
||||
type: string
|
||||
theme_version:
|
||||
description: "theme version"
|
||||
default: ""
|
||||
type: string
|
||||
|
||||
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 }}
|
||||
|
||||
- name: Free Disk Space (Ubuntu)
|
||||
uses: jlumbroso/free-disk-space@main
|
||||
with:
|
||||
swap-storage: false
|
||||
|
||||
# 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"
|
||||
|
||||
sudo apt update
|
||||
sudo apt install -y jq gawk cpio gettext libelf-dev qemu-utils busybox dialog curl sed
|
||||
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}" && "`echo ${LATEST_TAG} | cut -d '.' -f 1,2`" = "`date +'%y.%-m.%-d'`" ]]; then # format %y.%-m.$i
|
||||
VERSION="`echo ${LATEST_TAG} | awk -F '.' '{$3=$3+1}1' OFS='.'`"
|
||||
else
|
||||
VERSION="`date +'%y.%-m.%-d'`"
|
||||
fi
|
||||
fi
|
||||
|
||||
echo "Version: ${VERSION}"
|
||||
echo "VERSION=${VERSION}" >> $GITHUB_ENV
|
||||
|
||||
if [ -n "${VERSION}" ]; then
|
||||
# Modify Source File
|
||||
echo "${VERSION}" >VERSION
|
||||
echo "${VERSION}" >files/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"
|
||||
LKMTAG="${{ inputs.lkm_version }}"
|
||||
getLKMs "files/p3/lkms"
|
||||
echo "LKMTAG=${TAG}" >> $GITHUB_ENV
|
||||
ADDONSTAG="${{ inputs.addons_version }}"
|
||||
getAddons "files/p3/addons"
|
||||
echo "ADDONSTAG=${TAG}" >> $GITHUB_ENV
|
||||
MODULESTAG="${{ inputs.modules_version }}"
|
||||
getModules "files/p3/modules"
|
||||
echo "MODULESTAG=${TAG}" >> $GITHUB_ENV
|
||||
CONFIGSTAG="${{ inputs.configs_version }}"
|
||||
getConfigs "files/p3/configs"
|
||||
echo "CONFIGSTAG=${TAG}" >> $GITHUB_ENV
|
||||
PATCHESTAG="${{ inputs.patches_version }}"
|
||||
getPatches "files/p3/patches"
|
||||
echo "PATCHESTAG=${TAG}" >> $GITHUB_ENV
|
||||
THEMETAG="${{ inputs.theme_version }}"
|
||||
getTheme "files/p1/boot/grub"
|
||||
echo "THEMETAG=${TAG}" >> $GITHUB_ENV
|
||||
|
||||
echo "OK"
|
||||
|
||||
# Build incremental
|
||||
- name: Build Image
|
||||
run: |
|
||||
. scripts/func.sh
|
||||
|
||||
echo "Create Arc Image"
|
||||
IMAGE_FILE="arc.img"
|
||||
gzip -dc "files/initrd/opt/arc/grub.img.gz" >"${IMAGE_FILE}"
|
||||
fdisk -l "${IMAGE_FILE}"
|
||||
|
||||
LOOPX=$(sudo losetup -f)
|
||||
sudo losetup -P "${LOOPX}" "${IMAGE_FILE}"
|
||||
|
||||
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"
|
||||
br_version="2023.08.x"
|
||||
getBuildroot "${br_version}" "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
|
||||
if: success() && env.VERSION == ''
|
||||
uses: actions/upload-artifact@v4
|
||||
with:
|
||||
name: Images
|
||||
path: |
|
||||
arc.img.zip
|
||||
update.zip
|
||||
retention-days: 5
|
||||
|
||||
# Publish a release if is a tag
|
||||
- name: Release
|
||||
if: success() && env.VERSION != ''
|
||||
uses: ncipollo/release-action@v1
|
||||
with:
|
||||
tag: ${{ env.VERSION }}
|
||||
prerelease: ${{ inputs.prerelease }}
|
||||
allowUpdates: true
|
||||
body: |
|
||||
### Notice: ${{ inputs.notice }}
|
||||
|
||||
Buildroot: ${{ inputs.br_version }}
|
||||
Addons: ${{ env.ADDONSTAG }}
|
||||
Configs: ${{ env.CONFIGSTAG }}
|
||||
Modules: ${{ env.MODULESTAG }}
|
||||
Patches: ${{ env.PATCHESTAG }}
|
||||
Theme: ${{ env.THEMETAG }}
|
||||
LKM: ${{ env.LKMTAG }}
|
||||
|
||||
Changelog:
|
||||
${{ steps.Changelog.outputs.changelog }}
|
||||
artifacts: |
|
||||
arc-${{ env.VERSION }}.img.zip
|
||||
arc-${{ env.VERSION }}.vmdk-dyn.zip
|
||||
arc-${{ env.VERSION }}.vmdk-flat.zip
|
||||
update.zip
|
313
.github/workflows/custom.yml
vendored
Normal file
313
.github/workflows/custom.yml
vendored
Normal file
@ -0,0 +1,313 @@
|
||||
#
|
||||
# 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: Custom Build
|
||||
on:
|
||||
issues:
|
||||
types:
|
||||
- labeled
|
||||
|
||||
jobs:
|
||||
build:
|
||||
if: github.event.label.name == 'custom'
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- name: Checkout
|
||||
uses: actions/checkout@main
|
||||
|
||||
- name: Init Env
|
||||
run: |
|
||||
git config --global user.email "info@auxxxilium.tech"
|
||||
git config --global user.name "AuxXxilium"
|
||||
sudo timedatectl set-timezone "Europe/Berlin"
|
||||
|
||||
- name: Check Custom
|
||||
shell: python
|
||||
run: |
|
||||
# -*- coding: utf-8 -*-
|
||||
import json, subprocess
|
||||
def set_output(name, value):
|
||||
subprocess.call(["echo '{}={}' >> $GITHUB_ENV".format(name, value)], shell=True)
|
||||
|
||||
issuetitle = ${{ toJSON(github.event.issue.title) }};
|
||||
issuebody = ${{ toJSON(github.event.issue.body) }};
|
||||
|
||||
MODEL = ''
|
||||
PRODUCTVER = ''
|
||||
ADDONS = ''
|
||||
ARCPATCH = ''
|
||||
MACSYS = ''
|
||||
USBMOUNT = ''
|
||||
CUSTOMKERNEL = ''
|
||||
FORMAT = ''
|
||||
try:
|
||||
jsonbody = json.loads(issuebody)
|
||||
MODEL = jsonbody.get('model', '')
|
||||
PRODUCTVER = jsonbody.get('version', '')
|
||||
ADDONS = jsonbody.get('addons', '')
|
||||
ARCPATCH = jsonbody.get('patch', '')
|
||||
MACSYS = jsonbody.get('macsys', '')
|
||||
USBMOUNT = jsonbody.get('usbmount', '')
|
||||
CUSTOMKERNEL = jsonbody.get('customkernel', '')
|
||||
FORMAT = jsonbody.get('format', '')
|
||||
except ValueError as e:
|
||||
pass
|
||||
|
||||
set_output("MODEL", MODEL)
|
||||
set_output("PRODUCTVER", PRODUCTVER)
|
||||
set_output("ADDONS", ADDONS)
|
||||
set_output("ARCPATCH", ARCPATCH)
|
||||
set_output("MACSYS", MACSYS)
|
||||
set_output("USBMOUNT", USBMOUNT)
|
||||
set_output("CUSTOMKERNEL", CUSTOMKERNEL)
|
||||
set_output("FORMAT", FORMAT)
|
||||
|
||||
# calculates the version number and push
|
||||
- name: Calculate Version
|
||||
run: |
|
||||
# Calculate Version
|
||||
VERSION="`date +'%y.%-m.%-d'`-custom"
|
||||
|
||||
echo "Version: ${VERSION}"
|
||||
echo "VERSION=${VERSION}" >> $GITHUB_ENV
|
||||
|
||||
if [ -n "${VERSION}" ]; then
|
||||
# Modify Source File
|
||||
echo "${VERSION}" >VERSION
|
||||
echo "${VERSION}" >files/p1/ARC-VERSION
|
||||
sed 's/^ARC_VERSION=.*/ARC_VERSION="'${VERSION}'"/' -i files/initrd/opt/arc/include/consts.sh
|
||||
fi
|
||||
|
||||
- name: Update Comment Building
|
||||
id: comment
|
||||
uses: actions-cool/issues-helper@v3
|
||||
with:
|
||||
actions: 'create-comment'
|
||||
token: ${{ secrets.GITHUB_TOKEN }}
|
||||
issue-number: ${{ github.event.issue.number }}
|
||||
body: |
|
||||
Hi @${{ github.event.issue.user.login }}.
|
||||
arc-${{ env.MODEL }}-${{ env.PRODUCTVER }}-${{ env.VERSION }} is being build, it will be uploaded here and sent to your email later..
|
||||
> ${{ github.server_url }}/${{ github.repository }}/actions/runs/${{ github.run_id }}
|
||||
----
|
||||
|
||||
# Get extractor, LKM, Addons, Modules and Configs
|
||||
- name: Get extractor, LKM, Addons, Extensions, Modules and Configs
|
||||
run: |
|
||||
sudo apt update
|
||||
sudo apt install -y jq gawk cpio gettext libelf-dev qemu-utils busybox dialog curl sed
|
||||
sudo snap install yq
|
||||
|
||||
. scripts/func.sh
|
||||
|
||||
getExtractor "files/p3/extractor"
|
||||
getLKMs "files/p3/lkms"
|
||||
getAddons "files/p3/addons"
|
||||
getModules "files/p3/modules"
|
||||
getConfigs "files/p3/configs"
|
||||
getPatches "files/p3/patches"
|
||||
|
||||
echo "OK"
|
||||
|
||||
# Build incremental
|
||||
- name: Build image
|
||||
run: |
|
||||
. scripts/func.sh
|
||||
|
||||
function writeConfigKey() {
|
||||
[ "${2}" = "{}" ] && sudo yq eval '.'${1}' = {}' --inplace "${3}" 2>/dev/null || sudo yq eval '.'${1}' = "'"${2}"'"' --inplace "${3}" 2>/dev/null
|
||||
}
|
||||
|
||||
echo "Create Arc Image"
|
||||
IMAGE_FILE="arc.img"
|
||||
gzip -dc "files/initrd/opt/arc/grub.img.gz" >"${IMAGE_FILE}"
|
||||
fdisk -l "${IMAGE_FILE}"
|
||||
|
||||
LOOPX=$(sudo losetup -f)
|
||||
sudo losetup -P "${LOOPX}" "${IMAGE_FILE}"
|
||||
|
||||
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.08.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 "Set Custom Config"
|
||||
PRESET_CONFIG_FILE="files/p1/user-config.yml"
|
||||
touch "${PRESET_CONFIG_FILE}"
|
||||
writeConfigKey "model" "${{ env.MODEL }}" "${PRESET_CONFIG_FILE}"
|
||||
writeConfigKey "productver" "${{ env.PRODUCTVER }}" "${PRESET_CONFIG_FILE}"
|
||||
writeConfigKey "arc" "{}" "${PRESET_CONFIG_FILE}"
|
||||
writeConfigKey "arc.patch" "${{ env.ARCPATCH }}" "${PRESET_CONFIG_FILE}"
|
||||
writeConfigKey "arc.macsys" "${{ env.MACSYS }}" "${PRESET_CONFIG_FILE}"
|
||||
writeConfigKey "arc.custom" "true" "${PRESET_CONFIG_FILE}"
|
||||
writeConfigKey "arc.usbmount" "${{ env.USBMOUNT }}" "${PRESET_CONFIG_FILE}"
|
||||
if [ "${{ env.MODEL }}" = "SA6400" ]; then
|
||||
writeConfigKey "arc.kernel" "${{ env.KERNEL }}" "${PRESET_CONFIG_FILE}"
|
||||
fi
|
||||
writeConfigKey "addons" "{}" "${PRESET_CONFIG_FILE}"
|
||||
if [ -n "${{ env.ADDONS }}" ]; then
|
||||
writeConfigKey "addons" "{}" "${PRESET_CONFIG_FILE}"
|
||||
for A in $(echo "${{ env.ADDONS }}" | tr ',' ' ' | sed 's/\[//g' | sed 's/\]//g'); do
|
||||
VALUE=""
|
||||
writeConfigKey "addons.\"${A}\"" "${VALUE}" "${PRESET_CONFIG_FILE}"
|
||||
done
|
||||
fi
|
||||
echo "$(cat "${PRESET_CONFIG_FILE}")"
|
||||
echo "arc-${{ env.MODEL }}-${{ env.PRODUCTVER }}-${{ env.VERSION }}" >"files/p3/automated"
|
||||
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}
|
||||
|
||||
if [ "${{ env.FORMAT }}" = "dyn" ]; then
|
||||
echo "Image Converter -> dyn"
|
||||
qemu-img convert -O vmdk arc.img arc-dyn.vmdk
|
||||
elif [ "${{ env.FORMAT }}" = "flat" ]; then
|
||||
echo "Image Converter -> flat"
|
||||
qemu-img convert -O vmdk -o adapter_type=lsilogic arc.img -o subformat=monolithicFlat arc.vmdk
|
||||
fi
|
||||
|
||||
# Zip image and generate checksum
|
||||
- name: Pack
|
||||
run: |
|
||||
if [ "${{ env.FORMAT }}" = "img" ]; then
|
||||
zip -9 "arc-${{ env.MODEL }}-${{ env.PRODUCTVER }}-${{ env.VERSION }}.img.zip" arc.img
|
||||
UPLOAD="$(curl -k -F "file=@arc-${{ env.MODEL }}-${{ env.PRODUCTVER }}-${{ env.VERSION }}.img.zip" "https://file.io")"
|
||||
if ! echo "${UPLOAD}" | grep -oP '"link":\s*"\K[^"]+'; then
|
||||
echo "Upload failed"
|
||||
exit 1
|
||||
fi
|
||||
elif [ "${{ env.FORMAT }}" = "dyn" ]; then
|
||||
zip -9 "arc-${{ env.MODEL }}-${{ env.PRODUCTVER }}-${{ env.VERSION }}.vmdk-dyn.zip" arc-dyn.vmdk
|
||||
UPLOAD="$(curl -k -F "file=@arc-${{ env.MODEL }}-${{ env.PRODUCTVER }}-${{ env.VERSION }}.vmdk-dyn.zip" "https://file.io")"
|
||||
if ! echo "${UPLOAD}" | grep -oP '"link":\s*"\K[^"]+'; then
|
||||
echo "Upload failed"
|
||||
exit 1
|
||||
fi
|
||||
elif [ "${{ env.FORMAT }}" = "flat" ]; then
|
||||
zip -9 "arc-${{ env.MODEL }}-${{ env.PRODUCTVER }}-${{ env.VERSION }}.vmdk-flat.zip" arc.vmdk arc-flat.vmdk
|
||||
UPLOAD="$(curl -k -F "file=@arc-${{ env.MODEL }}-${{ env.PRODUCTVER }}-${{ env.VERSION }}.vmdk-flat.zip" "https://file.io")"
|
||||
if ! echo "${UPLOAD}" | grep -oP '"link":\s*"\K[^"]+'; then
|
||||
echo "Upload failed"
|
||||
exit 1
|
||||
fi
|
||||
fi
|
||||
|
||||
EMAIL=$(curl -s -H "Authorization: token ${{ secrets.ACTION }}" "https://api.github.com/users/${{ github.event.issue.user.login }}" | jq -r '.email')
|
||||
FIOURL="$(echo "${UPLOAD}" | grep -oP '"link":\s*"\K[^"]+')"
|
||||
echo "EMAIL=${EMAIL}" >> $GITHUB_ENV
|
||||
echo "FIOURL=${FIOURL}" >> $GITHUB_ENV
|
||||
|
||||
- name: Send mail
|
||||
if: success() && env.EMAIL != 'null'
|
||||
uses: dawidd6/action-send-mail@v3
|
||||
with:
|
||||
server_address: smtp.gmail.com
|
||||
server_port: 587
|
||||
username: ${{ secrets.MAIL_USERNAME }}
|
||||
password: ${{ secrets.MAIL_PASSWORD }}
|
||||
subject: arc-${{ env.model }}-${{ env.PRODUCTVER }}-${{ env.VERSION }}
|
||||
to: ${{ env.EMAIL }}
|
||||
from: ${{ secrets.MAIL_USERNAME }}
|
||||
body: |
|
||||
${{ env.FIOURL }}
|
||||
|
||||
- name: Update Comment Success
|
||||
if: success()
|
||||
uses: actions-cool/issues-helper@v3
|
||||
with:
|
||||
actions: 'update-comment'
|
||||
token: ${{ secrets.GITHUB_TOKEN }}
|
||||
comment-id: ${{ steps.comment.outputs.comment-id }}
|
||||
update-mode: replace
|
||||
body: |
|
||||
Hi @${{ github.event.issue.user.login }}.
|
||||
arc-${{ env.MODEL }}-${{ env.PRODUCTVER }}-${{ env.VERSION }} is ready to download: ${{ env.FIOURL }}
|
||||
> ${{ github.server_url }}/${{ github.repository }}/actions/runs/${{ github.run_id }}
|
||||
----
|
||||
|
||||
- name: Close Issues
|
||||
if: success()
|
||||
uses: actions-cool/issues-helper@v3
|
||||
with:
|
||||
actions: 'close-issue'
|
||||
token: ${{ secrets.GITHUB_TOKEN }}
|
||||
issue-number: ${{ github.event.issue.number }}
|
||||
|
||||
- name: Update Comment Fail
|
||||
if: failure()
|
||||
uses: actions-cool/issues-helper@v3
|
||||
with:
|
||||
actions: 'update-comment'
|
||||
token: ${{ secrets.GITHUB_TOKEN }}
|
||||
comment-id: ${{ steps.comment.outputs.comment-id }}
|
||||
update-mode: replace
|
||||
body: |
|
||||
Hi @${{ github.event.issue.user.login }}.
|
||||
arc-${{ env.MODEL }}-${{ env.PRODUCTVER }}-${{ env.VERSION }} failed to build and package, please try again.
|
||||
> ${{ github.server_url }}/${{ github.repository }}/actions/runs/${{ github.run_id }}
|
||||
----
|
||||
|
||||
comment:
|
||||
if: github.event.label.name == 'bug'
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- name: Init Env
|
||||
run: |
|
||||
git config --global user.email "info@auxxxilium.tech"
|
||||
git config --global user.name "AuxXxilium"
|
||||
sudo timedatectl set-timezone "Europe/Berlin"
|
||||
|
||||
- name: Check Issues
|
||||
run: |
|
||||
if echo "${{ github.event.issue.body }}" | grep -q "Screenshots"; then
|
||||
echo "FALG=true" >> $GITHUB_ENV
|
||||
else
|
||||
echo "FALG=false" >> $GITHUB_ENV
|
||||
fi
|
||||
|
||||
- name: Create Issues comment
|
||||
if: env.FALG == 'false'
|
||||
uses: actions-cool/issues-helper@v3
|
||||
with:
|
||||
actions: "create-comment"
|
||||
token: ${{ secrets.GITHUB_TOKEN }}
|
||||
issue-number: ${{ github.event.issue.number }}
|
||||
body: |
|
||||
**Describe the Bug**
|
||||
A clear and concise description of what the bug is.
|
||||
|
||||
**Screenshots**
|
||||
Screenshot the Systeminfo (Arc - Sysinfo) or use new "Full Sysinfo - Upload" Function provide the Code!!! <- IMPORTANT
|
||||
|
||||
**Hardware**
|
||||
Add your Hardware Informations here. CPU / Board / Networkcontroller / Storagecontroller / ...
|
||||
|
||||
- name: Close Issues
|
||||
if: env.FALG == 'false'
|
||||
uses: actions-cool/issues-helper@v3
|
||||
with:
|
||||
actions: 'close-issue'
|
||||
token: ${{ secrets.GITHUB_TOKEN }}
|
||||
issue-number: ${{ github.event.issue.number }}
|
60
.github/workflows/data.yml
vendored
Normal file
60
.github/workflows/data.yml
vendored
Normal file
@ -0,0 +1,60 @@
|
||||
#
|
||||
# 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: Data
|
||||
on:
|
||||
workflow_dispatch:
|
||||
inputs:
|
||||
push:
|
||||
description: "push"
|
||||
default: false
|
||||
type: boolean
|
||||
|
||||
jobs:
|
||||
build:
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- name: Checkout
|
||||
uses: actions/checkout@main
|
||||
|
||||
- name: Init Env
|
||||
run: |
|
||||
git config --global user.email "info@auxxxilium.tech"
|
||||
git config --global user.name "AuxXxilium"
|
||||
sudo timedatectl set-timezone "Europe/Berlin"
|
||||
|
||||
- name: Get models
|
||||
run: |
|
||||
. scripts/func.sh
|
||||
models="{"
|
||||
WORK_PATH="files/p3"
|
||||
mkdir -p "${WORK_PATH}/configs"
|
||||
getConfigs "${WORK_PATH}/configs"
|
||||
for M in $(find "${WORK_PATH}/configs" -maxdepth 1 -name \*.yml 2>/dev/null | sed 's/.*\///; s/\.yml//'); do
|
||||
models+="\"${M}\":["
|
||||
for V in $(yq eval '.productvers | explode(.) | to_entries | map([.key])[] | .[]' "${WORK_PATH}/configs/${M}.yml" 2>/dev/null | sort -r); do
|
||||
models+="\"${V}\","
|
||||
done
|
||||
models="${models%,}],"
|
||||
done
|
||||
models="${models%,}}"
|
||||
echo "${models}" >docs/models.json
|
||||
rm -rf "${WORK_PATH}/configs"
|
||||
|
||||
- name: Check and Push
|
||||
if: success() && inputs.push == true
|
||||
run: |
|
||||
echo "Git push ..."
|
||||
git pull
|
||||
status=$(git status -s | grep -E 'docs/models.json' | awk '{printf " %s", $2}')
|
||||
if [ -n "${status}" ]; then
|
||||
git add ${status}
|
||||
git commit -m "data: update $(date +%Y-%m-%d" "%H:%M:%S)"
|
||||
git push -f
|
||||
fi
|
||||
|
||||
|
62
.github/workflows/grub.yml
vendored
Normal file
62
.github/workflows/grub.yml
vendored
Normal file
@ -0,0 +1,62 @@
|
||||
#
|
||||
# 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 autoconf automake autopoint gettext build-essential bison flex libtool dosfstools e2fsprogs
|
||||
sudo apt install -y libfreetype6-dev unifont fonts-dejavu libfuse-dev liblzma-dev libzfslinux-dev
|
||||
|
||||
- name: Make Grub
|
||||
run: |
|
||||
. scripts/grub.sh "grub-2.12" "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/initrd/opt/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
|
||||
.vscode
|
||||
arpl.img*
|
||||
/buildroot-2022.02.2
|
||||
test.sh
|
||||
docker/Dockerfile
|
||||
docker/cache
|
||||
arc.img
|
||||
arc*.vmdk
|
||||
*.zip
|
||||
.buildroot
|
||||
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
|
24
README.md
24
README.md
@ -1 +1,23 @@
|
||||
# arpl
|
||||
<center><img width="845" alt="arc_loader" src="https://github.com/AuxXxilium/AuxXxilium/assets/67025065/ef975a36-9f3e-4cfb-813c-402db69611e7"></center>
|
||||
|
||||
Arc is a customized Redpill Loader for DSM 7.x (Xpenology) with enhanced Hardwaresupport, Addons, Extensions, guided(semi-automated) Installation and more. Multiple customization options are built-in. It is modified to run on different Hardware (see Wiki for more Informations).
|
||||
|
||||
- Commercial use is not permitted and strictly forbidden!!!
|
||||
- DSM and all Parts are under Copyright / Ownership or Registered Trademark by Synology Inc.
|
||||
|
||||
### 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) or use new "Full Sysinfo - Upload" Function and provide the Code!!! <- 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 latest Build</a>
|
||||
- <a href="https://auxxxilium.github.io/arc">Build your own</a>
|
||||
|
||||
### Thanks
|
||||
For code and ideas 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
|
||||
|
||||
version: '3'
|
||||
version: "3"
|
||||
|
||||
tasks:
|
||||
build-img:
|
||||
cmds:
|
||||
- ./img-gen.sh
|
||||
|
||||
build-docker-img:
|
||||
dir: docker
|
||||
compile-kpatch:
|
||||
dir: kpatch
|
||||
cmds:
|
||||
- ./build.sh
|
||||
|
||||
compile-lkms:
|
||||
cmds:
|
||||
- ./compile-lkm.sh
|
||||
|
||||
compile-addons:
|
||||
dir: addons
|
||||
cmds:
|
||||
- ./compile-addons.sh {{.CLI_ARGS}}
|
||||
- make clean all
|
||||
- mv kpatch ../files/board/arc/overlayfs/opt/arc/
|
@ -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
Some files were not shown because too many files have changed in this diff Show More
Loading…
Reference in New Issue
Block a user