mirror of
https://github.com/AuxXxilium/arc.git
synced 2024-11-23 19:19:52 +07:00
first commit
This commit is contained in:
commit
bad838d8b4
22
.editorconfig
Normal file
22
.editorconfig
Normal file
@ -0,0 +1,22 @@
|
||||
# top-most EditorConfig file
|
||||
root = true
|
||||
|
||||
# Unix-style newlines with a newline ending every file
|
||||
[*]
|
||||
end_of_line = lf
|
||||
insert_final_newline = true
|
||||
charset = utf-8
|
||||
indent_size = 2
|
||||
|
||||
# Tab indentation (no size specified)
|
||||
[Makefile]
|
||||
indent_style = tab
|
||||
|
||||
# YAML
|
||||
[*.yml]
|
||||
indent_style = space
|
||||
indent_size = 2
|
||||
|
||||
[*.{c,h}]
|
||||
indent_style = space
|
||||
indent_size = 4
|
7
.gitignore
vendored
Normal file
7
.gitignore
vendored
Normal file
@ -0,0 +1,7 @@
|
||||
!.gitkeep
|
||||
.vscode
|
||||
arpl.img*
|
||||
/buildroot-2022.02.2
|
||||
test.sh
|
||||
docker/Dockerfile
|
||||
docker/cache
|
8
PLATFORMS
Normal file
8
PLATFORMS
Normal file
@ -0,0 +1,8 @@
|
||||
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
|
23
TODO
Normal file
23
TODO
Normal file
@ -0,0 +1,23 @@
|
||||
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
|
22
Taskfile.yaml
Normal file
22
Taskfile.yaml
Normal file
@ -0,0 +1,22 @@
|
||||
# https://taskfile.dev
|
||||
|
||||
version: '3'
|
||||
|
||||
tasks:
|
||||
build-img:
|
||||
cmds:
|
||||
- ./img-gen.sh
|
||||
|
||||
build-docker-img:
|
||||
dir: docker
|
||||
cmds:
|
||||
- ./build.sh
|
||||
|
||||
compile-lkms:
|
||||
cmds:
|
||||
- ./compile-lkm.sh
|
||||
|
||||
compile-addons:
|
||||
dir: addons
|
||||
cmds:
|
||||
- ./compile-addons.sh {{.CLI_ARGS}}
|
6
addons/9p/install.sh
Normal file
6
addons/9p/install.sh
Normal file
@ -0,0 +1,6 @@
|
||||
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
|
28
addons/9p/manifest.yml
Normal file
28
addons/9p/manifest.yml
Normal file
@ -0,0 +1,28 @@
|
||||
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
|
36
addons/9p/src/3.10.108/Makefile
Normal file
36
addons/9p/src/3.10.108/Makefile
Normal file
@ -0,0 +1,36 @@
|
||||
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
|
375
addons/9p/src/3.10.108/acl.c
Normal file
375
addons/9p/src/3.10.108/acl.c
Normal file
@ -0,0 +1,375 @@
|
||||
/*
|
||||
* 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,
|
||||
};
|
55
addons/9p/src/3.10.108/acl.h
Normal file
55
addons/9p/src/3.10.108/acl.h
Normal file
@ -0,0 +1,55 @@
|
||||
/*
|
||||
* 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 */
|
415
addons/9p/src/3.10.108/cache.c
Normal file
415
addons/9p/src/3.10.108/cache.c
Normal file
@ -0,0 +1,415 @@
|
||||
/*
|
||||
* 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);
|
||||
}
|
139
addons/9p/src/3.10.108/cache.h
Normal file
139
addons/9p/src/3.10.108/cache.h
Normal file
@ -0,0 +1,139 @@
|
||||
/*
|
||||
* 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 */
|
2256
addons/9p/src/3.10.108/client.c
Normal file
2256
addons/9p/src/3.10.108/client.c
Normal file
File diff suppressed because it is too large
Load Diff
247
addons/9p/src/3.10.108/error.c
Normal file
247
addons/9p/src/3.10.108/error.c
Normal file
@ -0,0 +1,247 @@
|
||||
/*
|
||||
* 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);
|
306
addons/9p/src/3.10.108/fid.c
Normal file
306
addons/9p/src/3.10.108/fid.c
Normal file
@ -0,0 +1,306 @@
|
||||
/*
|
||||
* 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;
|
||||
}
|
30
addons/9p/src/3.10.108/fid.h
Normal file
30
addons/9p/src/3.10.108/fid.h
Normal file
@ -0,0 +1,30 @@
|
||||
/*
|
||||
* 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
|
201
addons/9p/src/3.10.108/mod.c
Normal file
201
addons/9p/src/3.10.108/mod.c
Normal file
@ -0,0 +1,201 @@
|
||||
/*
|
||||
* 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");
|
636
addons/9p/src/3.10.108/protocol.c
Normal file
636
addons/9p/src/3.10.108/protocol.c
Normal file
@ -0,0 +1,636 @@
|
||||
/*
|
||||
* 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);
|
34
addons/9p/src/3.10.108/protocol.h
Normal file
34
addons/9p/src/3.10.108/protocol.h
Normal file
@ -0,0 +1,34 @@
|
||||
/*
|
||||
* 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);
|
69
addons/9p/src/3.10.108/trans_common.c
Normal file
69
addons/9p/src/3.10.108/trans_common.c
Normal file
@ -0,0 +1,69 @@
|
||||
/*
|
||||
* 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);
|
17
addons/9p/src/3.10.108/trans_common.h
Normal file
17
addons/9p/src/3.10.108/trans_common.h
Normal file
@ -0,0 +1,17 @@
|
||||
/*
|
||||
* 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);
|
1090
addons/9p/src/3.10.108/trans_fd.c
Normal file
1090
addons/9p/src/3.10.108/trans_fd.c
Normal file
File diff suppressed because it is too large
Load Diff
719
addons/9p/src/3.10.108/trans_rdma.c
Normal file
719
addons/9p/src/3.10.108/trans_rdma.c
Normal file
@ -0,0 +1,719 @@
|
||||
/*
|
||||
* 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");
|
730
addons/9p/src/3.10.108/trans_virtio.c
Normal file
730
addons/9p/src/3.10.108/trans_virtio.c
Normal file
@ -0,0 +1,730 @@
|
||||
/*
|
||||
* 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");
|
141
addons/9p/src/3.10.108/util.c
Normal file
141
addons/9p/src/3.10.108/util.c
Normal file
@ -0,0 +1,141 @@
|
||||
/*
|
||||
* 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);
|
||||
|
677
addons/9p/src/3.10.108/v9fs.c
Normal file
677
addons/9p/src/3.10.108/v9fs.c
Normal file
@ -0,0 +1,677 @@
|
||||
/*
|
||||
* 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");
|
227
addons/9p/src/3.10.108/v9fs.h
Normal file
227
addons/9p/src/3.10.108/v9fs.h
Normal file
@ -0,0 +1,227 @@
|
||||
/*
|
||||
* 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
|
88
addons/9p/src/3.10.108/v9fs_vfs.h
Normal file
88
addons/9p/src/3.10.108/v9fs_vfs.h
Normal file
@ -0,0 +1,88 @@
|
||||
/*
|
||||
* 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
|
353
addons/9p/src/3.10.108/vfs_addr.c
Normal file
353
addons/9p/src/3.10.108/vfs_addr.c
Normal file
@ -0,0 +1,353 @@
|
||||
/*
|
||||
* 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,
|
||||
};
|
139
addons/9p/src/3.10.108/vfs_dentry.c
Normal file
139
addons/9p/src/3.10.108/vfs_dentry.c
Normal file
@ -0,0 +1,139 @@
|
||||
/*
|
||||
* 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,
|
||||
};
|
269
addons/9p/src/3.10.108/vfs_dir.c
Normal file
269
addons/9p/src/3.10.108/vfs_dir.c
Normal file
@ -0,0 +1,269 @@
|
||||
/*
|
||||
* 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,
|
||||
};
|
790
addons/9p/src/3.10.108/vfs_file.c
Normal file
790
addons/9p/src/3.10.108/vfs_file.c
Normal file
@ -0,0 +1,790 @@
|
||||
/*
|
||||
* 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,
|
||||
};
|
1538
addons/9p/src/3.10.108/vfs_inode.c
Normal file
1538
addons/9p/src/3.10.108/vfs_inode.c
Normal file
File diff suppressed because it is too large
Load Diff
1033
addons/9p/src/3.10.108/vfs_inode_dotl.c
Normal file
1033
addons/9p/src/3.10.108/vfs_inode_dotl.c
Normal file
File diff suppressed because it is too large
Load Diff
368
addons/9p/src/3.10.108/vfs_super.c
Normal file
368
addons/9p/src/3.10.108/vfs_super.c
Normal file
@ -0,0 +1,368 @@
|
||||
/*
|
||||
* 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");
|
175
addons/9p/src/3.10.108/xattr.c
Normal file
175
addons/9p/src/3.10.108/xattr.c
Normal file
@ -0,0 +1,175 @@
|
||||
/*
|
||||
* 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
|
||||
};
|
35
addons/9p/src/3.10.108/xattr.h
Normal file
35
addons/9p/src/3.10.108/xattr.h
Normal file
@ -0,0 +1,35 @@
|
||||
/*
|
||||
* 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 */
|
80
addons/9p/src/3.10.108/xattr_user.c
Normal file
80
addons/9p/src/3.10.108/xattr_user.c
Normal file
@ -0,0 +1,80 @@
|
||||
/*
|
||||
* 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,
|
||||
};
|
35
addons/9p/src/4.4.180/Makefile
Normal file
35
addons/9p/src/4.4.180/Makefile
Normal file
@ -0,0 +1,35 @@
|
||||
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
|
338
addons/9p/src/4.4.180/acl.c
Normal file
338
addons/9p/src/4.4.180/acl.c
Normal file
@ -0,0 +1,338 @@
|
||||
/*
|
||||
* 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,
|
||||
};
|
55
addons/9p/src/4.4.180/acl.h
Normal file
55
addons/9p/src/4.4.180/acl.h
Normal file
@ -0,0 +1,55 @@
|
||||
/*
|
||||
* 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 */
|
414
addons/9p/src/4.4.180/cache.c
Normal file
414
addons/9p/src/4.4.180/cache.c
Normal file
@ -0,0 +1,414 @@
|
||||
/*
|
||||
* 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);
|
||||
}
|
152
addons/9p/src/4.4.180/cache.h
Normal file
152
addons/9p/src/4.4.180/cache.h
Normal file
@ -0,0 +1,152 @@
|
||||
/*
|
||||
* 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 */
|
2297
addons/9p/src/4.4.180/client.c
Normal file
2297
addons/9p/src/4.4.180/client.c
Normal file
File diff suppressed because it is too large
Load Diff
247
addons/9p/src/4.4.180/error.c
Normal file
247
addons/9p/src/4.4.180/error.c
Normal file
@ -0,0 +1,247 @@
|
||||
/*
|
||||
* 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);
|
306
addons/9p/src/4.4.180/fid.c
Normal file
306
addons/9p/src/4.4.180/fid.c
Normal file
@ -0,0 +1,306 @@
|
||||
/*
|
||||
* 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;
|
||||
}
|
30
addons/9p/src/4.4.180/fid.h
Normal file
30
addons/9p/src/4.4.180/fid.h
Normal file
@ -0,0 +1,30 @@
|
||||
/*
|
||||
* 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
|
201
addons/9p/src/4.4.180/mod.c
Normal file
201
addons/9p/src/4.4.180/mod.c
Normal file
@ -0,0 +1,201 @@
|
||||
/*
|
||||
* 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");
|
634
addons/9p/src/4.4.180/protocol.c
Normal file
634
addons/9p/src/4.4.180/protocol.c
Normal file
@ -0,0 +1,634 @@
|
||||
/*
|
||||
* 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);
|
34
addons/9p/src/4.4.180/protocol.h
Normal file
34
addons/9p/src/4.4.180/protocol.h
Normal file
@ -0,0 +1,34 @@
|
||||
/*
|
||||
* 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);
|
29
addons/9p/src/4.4.180/trans_common.c
Normal file
29
addons/9p/src/4.4.180/trans_common.c
Normal file
@ -0,0 +1,29 @@
|
||||
/*
|
||||
* 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);
|
15
addons/9p/src/4.4.180/trans_common.h
Normal file
15
addons/9p/src/4.4.180/trans_common.h
Normal file
@ -0,0 +1,15 @@
|
||||
/*
|
||||
* 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);
|
1122
addons/9p/src/4.4.180/trans_fd.c
Normal file
1122
addons/9p/src/4.4.180/trans_fd.c
Normal file
File diff suppressed because it is too large
Load Diff
784
addons/9p/src/4.4.180/trans_rdma.c
Normal file
784
addons/9p/src/4.4.180/trans_rdma.c
Normal file
@ -0,0 +1,784 @@
|
||||
/*
|
||||
* 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");
|
788
addons/9p/src/4.4.180/trans_virtio.c
Normal file
788
addons/9p/src/4.4.180/trans_virtio.c
Normal file
@ -0,0 +1,788 @@
|
||||
/*
|
||||
* 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");
|
141
addons/9p/src/4.4.180/util.c
Normal file
141
addons/9p/src/4.4.180/util.c
Normal file
@ -0,0 +1,141 @@
|
||||
/*
|
||||
* 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);
|
||||
|
700
addons/9p/src/4.4.180/v9fs.c
Normal file
700
addons/9p/src/4.4.180/v9fs.c
Normal file
@ -0,0 +1,700 @@
|
||||
/*
|
||||
* 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");
|
226
addons/9p/src/4.4.180/v9fs.h
Normal file
226
addons/9p/src/4.4.180/v9fs.h
Normal file
@ -0,0 +1,226 @@
|
||||
/*
|
||||
* 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
|
105
addons/9p/src/4.4.180/v9fs_vfs.h
Normal file
105
addons/9p/src/4.4.180/v9fs_vfs.h
Normal file
@ -0,0 +1,105 @@
|
||||
/*
|
||||
* 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
|
351
addons/9p/src/4.4.180/vfs_addr.c
Normal file
351
addons/9p/src/4.4.180/vfs_addr.c
Normal file
@ -0,0 +1,351 @@
|
||||
/*
|
||||
* 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,
|
||||
};
|
122
addons/9p/src/4.4.180/vfs_dentry.c
Normal file
122
addons/9p/src/4.4.180/vfs_dentry.c
Normal file
@ -0,0 +1,122 @@
|
||||
/*
|
||||
* 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,
|
||||
};
|
248
addons/9p/src/4.4.180/vfs_dir.c
Normal file
248
addons/9p/src/4.4.180/vfs_dir.c
Normal file
@ -0,0 +1,248 @@
|
||||
/*
|
||||
* 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,
|
||||
};
|
726
addons/9p/src/4.4.180/vfs_file.c
Normal file
726
addons/9p/src/4.4.180/vfs_file.c
Normal file
@ -0,0 +1,726 @@
|
||||
/*
|
||||
* 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,
|
||||
};
|
1462
addons/9p/src/4.4.180/vfs_inode.c
Normal file
1462
addons/9p/src/4.4.180/vfs_inode.c
Normal file
File diff suppressed because it is too large
Load Diff
999
addons/9p/src/4.4.180/vfs_inode_dotl.c
Normal file
999
addons/9p/src/4.4.180/vfs_inode_dotl.c
Normal file
@ -0,0 +1,999 @@
|
||||
/*
|
||||
* 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,
|
||||
};
|
366
addons/9p/src/4.4.180/vfs_super.c
Normal file
366
addons/9p/src/4.4.180/vfs_super.c
Normal file
@ -0,0 +1,366 @@
|
||||
/*
|
||||
* 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");
|
195
addons/9p/src/4.4.180/xattr.c
Normal file
195
addons/9p/src/4.4.180/xattr.c
Normal file
@ -0,0 +1,195 @@
|
||||
/*
|
||||
* 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
|
||||
};
|
34
addons/9p/src/4.4.180/xattr.h
Normal file
34
addons/9p/src/4.4.180/xattr.h
Normal file
@ -0,0 +1,34 @@
|
||||
/*
|
||||
* 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 */
|
BIN
addons/acpid/all/addons/acpid.tgz
Normal file
BIN
addons/acpid/all/addons/acpid.tgz
Normal file
Binary file not shown.
12
addons/acpid/install.sh
Normal file
12
addons/acpid/install.sh
Normal file
@ -0,0 +1,12 @@
|
||||
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
|
36
addons/acpid/manifest.yml
Normal file
36
addons/acpid/manifest.yml
Normal file
@ -0,0 +1,36 @@
|
||||
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
|
46
addons/acpid/src/3.10.108/Makefile
Normal file
46
addons/acpid/src/3.10.108/Makefile
Normal file
@ -0,0 +1,46 @@
|
||||
#
|
||||
# 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
|
454
addons/acpid/src/3.10.108/button.c
Normal file
454
addons/acpid/src/3.10.108/button.c
Normal file
@ -0,0 +1,454 @@
|
||||
/*
|
||||
* 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
addons/acpid/src/4.4.180/Makefile
Normal file
1
addons/acpid/src/4.4.180/Makefile
Normal file
@ -0,0 +1 @@
|
||||
obj-$(CONFIG_ACPI_BUTTON) += button.o
|
0
addons/acpid/src/4.4.180/Module.symvers
Normal file
0
addons/acpid/src/4.4.180/Module.symvers
Normal file
449
addons/acpid/src/4.4.180/button.c
Normal file
449
addons/acpid/src/4.4.180/button.c
Normal file
@ -0,0 +1,449 @@
|
||||
/*
|
||||
* 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);
|
0
addons/acpid/src/4.4.180/modules.order
Normal file
0
addons/acpid/src/4.4.180/modules.order
Normal file
4
addons/atl1c/install.sh
Normal file
4
addons/atl1c/install.sh
Normal file
@ -0,0 +1,4 @@
|
||||
if [ "${1}" = "rd" ]; then
|
||||
echo "Installing module for Atheros L1C Gigabit Ethernet adapter"
|
||||
${INSMOD} "/modules/atl1c.ko" ${PARAMS}
|
||||
fi
|
28
addons/atl1c/manifest.yml
Normal file
28
addons/atl1c/manifest.yml
Normal file
@ -0,0 +1,28 @@
|
||||
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
|
2
addons/atl1c/src/3.10.108/Makefile
Normal file
2
addons/atl1c/src/3.10.108/Makefile
Normal file
@ -0,0 +1,2 @@
|
||||
obj-m += atl1c.o
|
||||
atl1c-objs := atl1c_main.o atl1c_hw.o atl1c_ethtool.o
|
606
addons/atl1c/src/3.10.108/atl1c.h
Normal file
606
addons/atl1c/src/3.10.108/atl1c.h
Normal file
@ -0,0 +1,606 @@
|
||||
/*
|
||||
* 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_ */
|
309
addons/atl1c/src/3.10.108/atl1c_ethtool.c
Normal file
309
addons/atl1c/src/3.10.108/atl1c_ethtool.c
Normal file
@ -0,0 +1,309 @@
|
||||
/*
|
||||
* 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);
|
||||
}
|
865
addons/atl1c/src/3.10.108/atl1c_hw.c
Normal file
865
addons/atl1c/src/3.10.108/atl1c_hw.c
Normal file
@ -0,0 +1,865 @@
|
||||
/*
|
||||
* 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);
|
||||
}
|
||||
}
|
||||
}
|
1024
addons/atl1c/src/3.10.108/atl1c_hw.h
Normal file
1024
addons/atl1c/src/3.10.108/atl1c_hw.h
Normal file
File diff suppressed because it is too large
Load Diff
2818
addons/atl1c/src/3.10.108/atl1c_main.c
Normal file
2818
addons/atl1c/src/3.10.108/atl1c_main.c
Normal file
File diff suppressed because it is too large
Load Diff
2
addons/atl1c/src/4.4.180/Makefile
Normal file
2
addons/atl1c/src/4.4.180/Makefile
Normal file
@ -0,0 +1,2 @@
|
||||
obj-m += atl1c.o
|
||||
atl1c-objs := atl1c_main.o atl1c_hw.o atl1c_ethtool.o
|
605
addons/atl1c/src/4.4.180/atl1c.h
Normal file
605
addons/atl1c/src/4.4.180/atl1c.h
Normal file
@ -0,0 +1,605 @@
|
||||
/*
|
||||
* 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_ */
|
305
addons/atl1c/src/4.4.180/atl1c_ethtool.c
Normal file
305
addons/atl1c/src/4.4.180/atl1c_ethtool.c
Normal file
@ -0,0 +1,305 @@
|
||||
/*
|
||||
* 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;
|
||||
}
|
863
addons/atl1c/src/4.4.180/atl1c_hw.c
Normal file
863
addons/atl1c/src/4.4.180/atl1c_hw.c
Normal file
@ -0,0 +1,863 @@
|
||||
/*
|
||||
* 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);
|
||||
}
|
||||
}
|
||||
}
|
1024
addons/atl1c/src/4.4.180/atl1c_hw.h
Normal file
1024
addons/atl1c/src/4.4.180/atl1c_hw.h
Normal file
File diff suppressed because it is too large
Load Diff
2813
addons/atl1c/src/4.4.180/atl1c_main.c
Normal file
2813
addons/atl1c/src/4.4.180/atl1c_main.c
Normal file
File diff suppressed because it is too large
Load Diff
4
addons/atl1e/install.sh
Normal file
4
addons/atl1e/install.sh
Normal file
@ -0,0 +1,4 @@
|
||||
if [ "${1}" = "rd" ]; then
|
||||
echo "Installing module for Atheros L1E Gigabit Ethernet adapter"
|
||||
${INSMOD} "/modules/atl1e.ko" ${PARAMS}
|
||||
fi
|
28
addons/atl1e/manifest.yml
Normal file
28
addons/atl1e/manifest.yml
Normal file
@ -0,0 +1,28 @@
|
||||
version: 1
|
||||
name: atl1e
|
||||
description: "Driver for Atheros L1E Gigabit Ethernet adapters"
|
||||
available-for:
|
||||
bromolow-3.10.108:
|
||||
install-script: &script "install.sh"
|
||||
modules: true
|
||||
apollolake-4.4.180:
|
||||
install-script: *script
|
||||
modules: true
|
||||
broadwell-4.4.180:
|
||||
install-script: *script
|
||||
modules: true
|
||||
broadwellnk-4.4.180:
|
||||
install-script: *script
|
||||
modules: true
|
||||
denverton-4.4.180:
|
||||
install-script: *script
|
||||
modules: true
|
||||
geminilake-4.4.180:
|
||||
install-script: *script
|
||||
modules: true
|
||||
v1000-4.4.180:
|
||||
install-script: *script
|
||||
modules: true
|
||||
purley-4.4.180:
|
||||
install-script: *script
|
||||
modules: true
|
2
addons/atl1e/src/3.10.108/Makefile
Normal file
2
addons/atl1e/src/3.10.108/Makefile
Normal file
@ -0,0 +1,2 @@
|
||||
obj-m += atl1e.o
|
||||
atl1e-objs += atl1e_main.o atl1e_hw.o atl1e_ethtool.o atl1e_param.o
|
508
addons/atl1e/src/3.10.108/atl1e.h
Normal file
508
addons/atl1e/src/3.10.108/atl1e.h
Normal file
@ -0,0 +1,508 @@
|
||||
/*
|
||||
* Copyright(c) 2007 Atheros Corporation. All rights reserved.
|
||||
* Copyright(c) 2007 xiong huang <xiong.huang@atheros.com>
|
||||
*
|
||||
* 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 _ATL1E_H_
|
||||
#define _ATL1E_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 "atl1e_hw.h"
|
||||
|
||||
#define PCI_REG_COMMAND 0x04 /* PCI Command Register */
|
||||
#define CMD_IO_SPACE 0x0001
|
||||
#define CMD_MEMORY_SPACE 0x0002
|
||||
#define CMD_BUS_MASTER 0x0004
|
||||
|
||||
#define BAR_0 0
|
||||
#define BAR_1 1
|
||||
#define BAR_5 5
|
||||
|
||||
/* 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 SPEED_0 0xffff
|
||||
#define HALF_DUPLEX 1
|
||||
#define FULL_DUPLEX 2
|
||||
|
||||
/* Error Codes */
|
||||
#define AT_ERR_EEPROM 1
|
||||
#define AT_ERR_PHY 2
|
||||
#define AT_ERR_CONFIG 3
|
||||
#define AT_ERR_PARAM 4
|
||||
#define AT_ERR_MAC_TYPE 5
|
||||
#define AT_ERR_PHY_TYPE 6
|
||||
#define AT_ERR_PHY_SPEED 7
|
||||
#define AT_ERR_PHY_RES 8
|
||||
#define AT_ERR_TIMEOUT 9
|
||||
|
||||
#define MAX_JUMBO_FRAME_SIZE 0x2000
|
||||
|
||||
#define AT_VLAN_TAG_TO_TPD_TAG(_vlan, _tpd) \
|
||||
_tpd = (((_vlan) << (4)) | (((_vlan) >> 13) & 7) |\
|
||||
(((_vlan) >> 9) & 8))
|
||||
|
||||
#define AT_TPD_TAG_TO_VLAN_TAG(_tpd, _vlan) \
|
||||
_vlan = (((_tpd) >> 8) | (((_tpd) & 0x77) << 9) |\
|
||||
(((_tdp) & 0x88) << 5))
|
||||
|
||||
#define AT_MAX_RECEIVE_QUEUE 4
|
||||
#define AT_PAGE_NUM_PER_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 10
|
||||
#define AT_TWSI_EEPROM_TIMEOUT 100
|
||||
#define AT_HW_MAX_IDLE_DELAY 10
|
||||
#define AT_SUSPEND_LINK_TIMEOUT 28
|
||||
|
||||
#define AT_REGS_LEN 75
|
||||
#define AT_EEPROM_LEN 512
|
||||
#define AT_ADV_MASK (ADVERTISE_10_HALF |\
|
||||
ADVERTISE_10_FULL |\
|
||||
ADVERTISE_100_HALF |\
|
||||
ADVERTISE_100_FULL |\
|
||||
ADVERTISE_1000_FULL)
|
||||
|
||||
/* tpd word 2 */
|
||||
#define TPD_BUFLEN_MASK 0x3FFF
|
||||
#define TPD_BUFLEN_SHIFT 0
|
||||
#define TPD_DMAINT_MASK 0x0001
|
||||
#define TPD_DMAINT_SHIFT 14
|
||||
#define TPD_PKTNT_MASK 0x0001
|
||||
#define TPD_PKTINT_SHIFT 15
|
||||
#define TPD_VLANTAG_MASK 0xFFFF
|
||||
#define TPD_VLAN_SHIFT 16
|
||||
|
||||
/* tpd word 3 bits 0:4 */
|
||||
#define TPD_EOP_MASK 0x0001
|
||||
#define TPD_EOP_SHIFT 0
|
||||
#define TPD_IP_VERSION_MASK 0x0001
|
||||
#define TPD_IP_VERSION_SHIFT 1 /* 0 : IPV4, 1 : IPV6 */
|
||||
#define TPD_INS_VL_TAG_MASK 0x0001
|
||||
#define TPD_INS_VL_TAG_SHIFT 2
|
||||
#define TPD_CC_SEGMENT_EN_MASK 0x0001
|
||||
#define TPD_CC_SEGMENT_EN_SHIFT 3
|
||||
#define TPD_SEGMENT_EN_MASK 0x0001
|
||||
#define TPD_SEGMENT_EN_SHIFT 4
|
||||
|
||||
/* tdp word 3 bits 5:7 if ip version is 0 */
|
||||
#define TPD_IP_CSUM_MASK 0x0001
|
||||
#define TPD_IP_CSUM_SHIFT 5
|
||||
#define TPD_TCP_CSUM_MASK 0x0001
|
||||
#define TPD_TCP_CSUM_SHIFT 6
|
||||
#define TPD_UDP_CSUM_MASK 0x0001
|
||||
#define TPD_UDP_CSUM_SHIFT 7
|
||||
|
||||
/* tdp word 3 bits 5:7 if ip version is 1 */
|
||||
#define TPD_V6_IPHLLO_MASK 0x0007
|
||||
#define TPD_V6_IPHLLO_SHIFT 7
|
||||
|
||||
/* tpd word 3 bits 8:9 bit */
|
||||
#define TPD_VL_TAGGED_MASK 0x0001
|
||||
#define TPD_VL_TAGGED_SHIFT 8
|
||||
#define TPD_ETHTYPE_MASK 0x0001
|
||||
#define TPD_ETHTYPE_SHIFT 9
|
||||
|
||||
/* tdp word 3 bits 10:13 if ip version is 0 */
|
||||
#define TDP_V4_IPHL_MASK 0x000F
|
||||
#define TPD_V4_IPHL_SHIFT 10
|
||||
|
||||
/* tdp word 3 bits 10:13 if ip version is 1 */
|
||||
#define TPD_V6_IPHLHI_MASK 0x000F
|
||||
#define TPD_V6_IPHLHI_SHIFT 10
|
||||
|
||||
/* tpd word 3 bit 14:31 if segment enabled */
|
||||
#define TPD_TCPHDRLEN_MASK 0x000F
|
||||
#define TPD_TCPHDRLEN_SHIFT 14
|
||||
#define TPD_HDRFLAG_MASK 0x0001
|
||||
#define TPD_HDRFLAG_SHIFT 18
|
||||
#define TPD_MSS_MASK 0x1FFF
|
||||
#define TPD_MSS_SHIFT 19
|
||||
|
||||
/* tdp word 3 bit 16:31 if custom csum enabled */
|
||||
#define TPD_PLOADOFFSET_MASK 0x00FF
|
||||
#define TPD_PLOADOFFSET_SHIFT 16
|
||||
#define TPD_CCSUMOFFSET_MASK 0x00FF
|
||||
#define TPD_CCSUMOFFSET_SHIFT 24
|
||||
|
||||
struct atl1e_tpd_desc {
|
||||
__le64 buffer_addr;
|
||||
__le32 word2;
|
||||
__le32 word3;
|
||||
};
|
||||
|
||||
/* how about 0x2000 */
|
||||
#define MAX_TX_BUF_LEN 0x2000
|
||||
#define MAX_TX_BUF_SHIFT 13
|
||||
#define MAX_TSO_SEG_SIZE 0x3c00
|
||||
|
||||
/* rrs word 1 bit 0:31 */
|
||||
#define RRS_RX_CSUM_MASK 0xFFFF
|
||||
#define RRS_RX_CSUM_SHIFT 0
|
||||
#define RRS_PKT_SIZE_MASK 0x3FFF
|
||||
#define RRS_PKT_SIZE_SHIFT 16
|
||||
#define RRS_CPU_NUM_MASK 0x0003
|
||||
#define RRS_CPU_NUM_SHIFT 30
|
||||
|
||||
#define RRS_IS_RSS_IPV4 0x0001
|
||||
#define RRS_IS_RSS_IPV4_TCP 0x0002
|
||||
#define RRS_IS_RSS_IPV6 0x0004
|
||||
#define RRS_IS_RSS_IPV6_TCP 0x0008
|
||||
#define RRS_IS_IPV6 0x0010
|
||||
#define RRS_IS_IP_FRAG 0x0020
|
||||
#define RRS_IS_IP_DF 0x0040
|
||||
#define RRS_IS_802_3 0x0080
|
||||
#define RRS_IS_VLAN_TAG 0x0100
|
||||
#define RRS_IS_ERR_FRAME 0x0200
|
||||
#define RRS_IS_IPV4 0x0400
|
||||
#define RRS_IS_UDP 0x0800
|
||||
#define RRS_IS_TCP 0x1000
|
||||
#define RRS_IS_BCAST 0x2000
|
||||
#define RRS_IS_MCAST 0x4000
|
||||
#define RRS_IS_PAUSE 0x8000
|
||||
|
||||
#define RRS_ERR_BAD_CRC 0x0001
|
||||
#define RRS_ERR_CODE 0x0002
|
||||
#define RRS_ERR_DRIBBLE 0x0004
|
||||
#define RRS_ERR_RUNT 0x0008
|
||||
#define RRS_ERR_RX_OVERFLOW 0x0010
|
||||
#define RRS_ERR_TRUNC 0x0020
|
||||
#define RRS_ERR_IP_CSUM 0x0040
|
||||
#define RRS_ERR_L4_CSUM 0x0080
|
||||
#define RRS_ERR_LENGTH 0x0100
|
||||
#define RRS_ERR_DES_ADDR 0x0200
|
||||
|
||||
struct atl1e_recv_ret_status {
|
||||
u16 seq_num;
|
||||
u16 hash_lo;
|
||||
__le32 word1;
|
||||
u16 pkt_flag;
|
||||
u16 err_flag;
|
||||
u16 hash_hi;
|
||||
u16 vtag;
|
||||
};
|
||||
|
||||
enum atl1e_dma_req_block {
|
||||
atl1e_dma_req_128 = 0,
|
||||
atl1e_dma_req_256 = 1,
|
||||
atl1e_dma_req_512 = 2,
|
||||
atl1e_dma_req_1024 = 3,
|
||||
atl1e_dma_req_2048 = 4,
|
||||
atl1e_dma_req_4096 = 5
|
||||
};
|
||||
|
||||
enum atl1e_rrs_type {
|
||||
atl1e_rrs_disable = 0,
|
||||
atl1e_rrs_ipv4 = 1,
|
||||
atl1e_rrs_ipv4_tcp = 2,
|
||||
atl1e_rrs_ipv6 = 4,
|
||||
atl1e_rrs_ipv6_tcp = 8
|
||||
};
|
||||
|
||||
enum atl1e_nic_type {
|
||||
athr_l1e = 0,
|
||||
athr_l2e_revA = 1,
|
||||
athr_l2e_revB = 2
|
||||
};
|
||||
|
||||
struct atl1e_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 atl1e_hw {
|
||||
u8 __iomem *hw_addr; /* inner register address */
|
||||
resource_size_t mem_rang;
|
||||
struct atl1e_adapter *adapter;
|
||||
enum atl1e_nic_type nic_type;
|
||||
u16 device_id;
|
||||
u16 vendor_id;
|
||||
u16 subsystem_id;
|
||||
u16 subsystem_vendor_id;
|
||||
u8 revision_id;
|
||||
u16 pci_cmd_word;
|
||||
u8 mac_addr[ETH_ALEN];
|
||||
u8 perm_mac_addr[ETH_ALEN];
|
||||
u8 preamble_len;
|
||||
u16 max_frame_size;
|
||||
u16 rx_jumbo_th;
|
||||
u16 tx_jumbo_th;
|
||||
|
||||
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;
|
||||
#define ADVERTISE_10_HALF 0x0001
|
||||
#define ADVERTISE_10_FULL 0x0002
|
||||
#define ADVERTISE_100_HALF 0x0004
|
||||
#define ADVERTISE_100_FULL 0x0008
|
||||
#define ADVERTISE_1000_HALF 0x0010 /* Not used, just FYI */
|
||||
#define ADVERTISE_1000_FULL 0x0020
|
||||
u16 mii_autoneg_adv_reg;
|
||||
u16 mii_1000t_ctrl_reg;
|
||||
|
||||
u16 imt; /* Interrupt Moderator timer ( 2us resolution) */
|
||||
u16 ict; /* Interrupt Clear timer (2us resolution) */
|
||||
u32 smb_timer;
|
||||
u16 rrd_thresh; /* Threshold of number of RRD produced to trigger
|
||||
interrupt request */
|
||||
u16 tpd_thresh;
|
||||
u16 rx_count_down; /* 2us resolution */
|
||||
u16 tx_count_down;
|
||||
|
||||
u8 tpd_burst; /* Number of TPD to prefetch in cache-aligned burst. */
|
||||
enum atl1e_rrs_type rrs_type;
|
||||
u32 base_cpu;
|
||||
u32 indirect_tab;
|
||||
|
||||
enum atl1e_dma_req_block dmar_block;
|
||||
enum atl1e_dma_req_block dmaw_block;
|
||||
u8 dmaw_dly_cnt;
|
||||
u8 dmar_dly_cnt;
|
||||
|
||||
bool phy_configured;
|
||||
bool re_autoneg;
|
||||
bool emi_ca;
|
||||
};
|
||||
|
||||
/*
|
||||
* wrapper around a pointer to a socket buffer,
|
||||
* so a DMA handle can be stored along with the buffer
|
||||
*/
|
||||
struct atl1e_tx_buffer {
|
||||
struct sk_buff *skb;
|
||||
u16 flags;
|
||||
#define ATL1E_TX_PCIMAP_SINGLE 0x0001
|
||||
#define ATL1E_TX_PCIMAP_PAGE 0x0002
|
||||
#define ATL1E_TX_PCIMAP_TYPE_MASK 0x0003
|
||||
u16 length;
|
||||
dma_addr_t dma;
|
||||
};
|
||||
|
||||
#define ATL1E_SET_PCIMAP_TYPE(tx_buff, type) do { \
|
||||
((tx_buff)->flags) &= ~ATL1E_TX_PCIMAP_TYPE_MASK; \
|
||||
((tx_buff)->flags) |= (type); \
|
||||
} while (0)
|
||||
|
||||
struct atl1e_rx_page {
|
||||
dma_addr_t dma; /* receive rage DMA address */
|
||||
u8 *addr; /* receive rage virtual address */
|
||||
dma_addr_t write_offset_dma; /* the DMA address which contain the
|
||||
receive data offset in the page */
|
||||
u32 *write_offset_addr; /* the virtaul address which contain
|
||||
the receive data offset in the page */
|
||||
u32 read_offset; /* the offset where we have read */
|
||||
};
|
||||
|
||||
struct atl1e_rx_page_desc {
|
||||
struct atl1e_rx_page rx_page[AT_PAGE_NUM_PER_QUEUE];
|
||||
u8 rx_using;
|
||||
u16 rx_nxseq;
|
||||
};
|
||||
|
||||
/* transmit packet descriptor (tpd) ring */
|
||||
struct atl1e_tx_ring {
|
||||
struct atl1e_tpd_desc *desc; /* descriptor ring virtual address */
|
||||
dma_addr_t dma; /* descriptor ring physical address */
|
||||
u16 count; /* the count of transmit rings */
|
||||
rwlock_t tx_lock;
|
||||
u16 next_to_use;
|
||||
atomic_t next_to_clean;
|
||||
struct atl1e_tx_buffer *tx_buffer;
|
||||
dma_addr_t cmb_dma;
|
||||
u32 *cmb;
|
||||
};
|
||||
|
||||
/* receive packet descriptor ring */
|
||||
struct atl1e_rx_ring {
|
||||
void *desc;
|
||||
dma_addr_t dma;
|
||||
int size;
|
||||
u32 page_size; /* bytes length of rxf page */
|
||||
u32 real_page_size; /* real_page_size = page_size + jumbo + aliagn */
|
||||
struct atl1e_rx_page_desc rx_page_desc[AT_MAX_RECEIVE_QUEUE];
|
||||
};
|
||||
|
||||
/* board specific private data structure */
|
||||
struct atl1e_adapter {
|
||||
struct net_device *netdev;
|
||||
struct pci_dev *pdev;
|
||||
struct napi_struct napi;
|
||||
struct mii_if_info mii; /* MII interface info */
|
||||
struct atl1e_hw hw;
|
||||
struct atl1e_hw_stats hw_stats;
|
||||
|
||||
u32 wol;
|
||||
u16 link_speed;
|
||||
u16 link_duplex;
|
||||
|
||||
spinlock_t mdio_lock;
|
||||
spinlock_t tx_lock;
|
||||
atomic_t irq_sem;
|
||||
|
||||
struct work_struct reset_task;
|
||||
struct work_struct link_chg_task;
|
||||
struct timer_list watchdog_timer;
|
||||
struct timer_list phy_config_timer;
|
||||
|
||||
/* All Descriptor memory */
|
||||
dma_addr_t ring_dma;
|
||||
void *ring_vir_addr;
|
||||
u32 ring_size;
|
||||
|
||||
struct atl1e_tx_ring tx_ring;
|
||||
struct atl1e_rx_ring rx_ring;
|
||||
int num_rx_queues;
|
||||
unsigned long flags;
|
||||
#define __AT_TESTING 0x0001
|
||||
#define __AT_RESETTING 0x0002
|
||||
#define __AT_DOWN 0x0003
|
||||
|
||||
u32 bd_number; /* board number;*/
|
||||
u32 pci_state[16];
|
||||
u32 *config_space;
|
||||
};
|
||||
|
||||
#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) ( \
|
||||
readl((a)->hw_addr + reg))
|
||||
|
||||
#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) (\
|
||||
readw((a)->hw_addr + reg))
|
||||
|
||||
#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 atl1e_driver_name[];
|
||||
extern char atl1e_driver_version[];
|
||||
|
||||
extern void atl1e_check_options(struct atl1e_adapter *adapter);
|
||||
extern int atl1e_up(struct atl1e_adapter *adapter);
|
||||
extern void atl1e_down(struct atl1e_adapter *adapter);
|
||||
extern void atl1e_reinit_locked(struct atl1e_adapter *adapter);
|
||||
extern s32 atl1e_reset_hw(struct atl1e_hw *hw);
|
||||
extern void atl1e_set_ethtool_ops(struct net_device *netdev);
|
||||
#endif /* _ATL1_E_H_ */
|
392
addons/atl1e/src/3.10.108/atl1e_ethtool.c
Normal file
392
addons/atl1e/src/3.10.108/atl1e_ethtool.c
Normal file
@ -0,0 +1,392 @@
|
||||
/*
|
||||
* 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/netdevice.h>
|
||||
#include <linux/ethtool.h>
|
||||
#include <linux/slab.h>
|
||||
|
||||
#include "atl1e.h"
|
||||
|
||||
static int atl1e_get_settings(struct net_device *netdev,
|
||||
struct ethtool_cmd *ecmd)
|
||||
{
|
||||
struct atl1e_adapter *adapter = netdev_priv(netdev);
|
||||
struct atl1e_hw *hw = &adapter->hw;
|
||||
|
||||
ecmd->supported = (SUPPORTED_10baseT_Half |
|
||||
SUPPORTED_10baseT_Full |
|
||||
SUPPORTED_100baseT_Half |
|
||||
SUPPORTED_100baseT_Full |
|
||||
SUPPORTED_Autoneg |
|
||||
SUPPORTED_TP);
|
||||
if (hw->nic_type == athr_l1e)
|
||||
ecmd->supported |= SUPPORTED_1000baseT_Full;
|
||||
|
||||
ecmd->advertising = ADVERTISED_TP;
|
||||
|
||||
ecmd->advertising |= ADVERTISED_Autoneg;
|
||||
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 atl1e_set_settings(struct net_device *netdev,
|
||||
struct ethtool_cmd *ecmd)
|
||||
{
|
||||
struct atl1e_adapter *adapter = netdev_priv(netdev);
|
||||
struct atl1e_hw *hw = &adapter->hw;
|
||||
|
||||
while (test_and_set_bit(__AT_RESETTING, &adapter->flags))
|
||||
msleep(1);
|
||||
|
||||
if (ecmd->autoneg == AUTONEG_ENABLE) {
|
||||
u16 adv4, adv9;
|
||||
|
||||
if ((ecmd->advertising&ADVERTISE_1000_FULL)) {
|
||||
if (hw->nic_type == athr_l1e) {
|
||||
hw->autoneg_advertised =
|
||||
ecmd->advertising & AT_ADV_MASK;
|
||||
} else {
|
||||
clear_bit(__AT_RESETTING, &adapter->flags);
|
||||
return -EINVAL;
|
||||
}
|
||||
} else if (ecmd->advertising&ADVERTISE_1000_HALF) {
|
||||
clear_bit(__AT_RESETTING, &adapter->flags);
|
||||
return -EINVAL;
|
||||
} else {
|
||||
hw->autoneg_advertised =
|
||||
ecmd->advertising & AT_ADV_MASK;
|
||||
}
|
||||
ecmd->advertising = hw->autoneg_advertised |
|
||||
ADVERTISED_TP | ADVERTISED_Autoneg;
|
||||
|
||||
adv4 = hw->mii_autoneg_adv_reg & ~ADVERTISE_ALL;
|
||||
adv9 = hw->mii_1000t_ctrl_reg & ~MII_AT001_CR_1000T_SPEED_MASK;
|
||||
if (hw->autoneg_advertised & ADVERTISE_10_HALF)
|
||||
adv4 |= ADVERTISE_10HALF;
|
||||
if (hw->autoneg_advertised & ADVERTISE_10_FULL)
|
||||
adv4 |= ADVERTISE_10FULL;
|
||||
if (hw->autoneg_advertised & ADVERTISE_100_HALF)
|
||||
adv4 |= ADVERTISE_100HALF;
|
||||
if (hw->autoneg_advertised & ADVERTISE_100_FULL)
|
||||
adv4 |= ADVERTISE_100FULL;
|
||||
if (hw->autoneg_advertised & ADVERTISE_1000_FULL)
|
||||
adv9 |= ADVERTISE_1000FULL;
|
||||
|
||||
if (adv4 != hw->mii_autoneg_adv_reg ||
|
||||
adv9 != hw->mii_1000t_ctrl_reg) {
|
||||
hw->mii_autoneg_adv_reg = adv4;
|
||||
hw->mii_1000t_ctrl_reg = adv9;
|
||||
hw->re_autoneg = true;
|
||||
}
|
||||
|
||||
} else {
|
||||
clear_bit(__AT_RESETTING, &adapter->flags);
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
/* reset the link */
|
||||
|
||||
if (netif_running(adapter->netdev)) {
|
||||
atl1e_down(adapter);
|
||||
atl1e_up(adapter);
|
||||
} else
|
||||
atl1e_reset_hw(&adapter->hw);
|
||||
|
||||
clear_bit(__AT_RESETTING, &adapter->flags);
|
||||
return 0;
|
||||
}
|
||||
|
||||
static u32 atl1e_get_msglevel(struct net_device *netdev)
|
||||
{
|
||||
#ifdef DBG
|
||||
return 1;
|
||||
#else
|
||||
return 0;
|
||||
#endif
|
||||
}
|
||||
|
||||
static int atl1e_get_regs_len(struct net_device *netdev)
|
||||
{
|
||||
return AT_REGS_LEN * sizeof(u32);
|
||||
}
|
||||
|
||||
static void atl1e_get_regs(struct net_device *netdev,
|
||||
struct ethtool_regs *regs, void *p)
|
||||
{
|
||||
struct atl1e_adapter *adapter = netdev_priv(netdev);
|
||||
struct atl1e_hw *hw = &adapter->hw;
|
||||
u32 *regs_buff = p;
|
||||
u16 phy_data;
|
||||
|
||||
memset(p, 0, AT_REGS_LEN * sizeof(u32));
|
||||
|
||||
regs->version = (1 << 24) | (hw->revision_id << 16) | hw->device_id;
|
||||
|
||||
regs_buff[0] = AT_READ_REG(hw, REG_VPD_CAP);
|
||||
regs_buff[1] = AT_READ_REG(hw, REG_SPI_FLASH_CTRL);
|
||||
regs_buff[2] = AT_READ_REG(hw, REG_SPI_FLASH_CONFIG);
|
||||
regs_buff[3] = AT_READ_REG(hw, REG_TWSI_CTRL);
|
||||
regs_buff[4] = AT_READ_REG(hw, REG_PCIE_DEV_MISC_CTRL);
|
||||
regs_buff[5] = AT_READ_REG(hw, REG_MASTER_CTRL);
|
||||
regs_buff[6] = AT_READ_REG(hw, REG_MANUAL_TIMER_INIT);
|
||||
regs_buff[7] = AT_READ_REG(hw, REG_IRQ_MODU_TIMER_INIT);
|
||||
regs_buff[8] = AT_READ_REG(hw, REG_GPHY_CTRL);
|
||||
regs_buff[9] = AT_READ_REG(hw, REG_CMBDISDMA_TIMER);
|
||||
regs_buff[10] = AT_READ_REG(hw, REG_IDLE_STATUS);
|
||||
regs_buff[11] = AT_READ_REG(hw, REG_MDIO_CTRL);
|
||||
regs_buff[12] = AT_READ_REG(hw, REG_SERDES_LOCK);
|
||||
regs_buff[13] = AT_READ_REG(hw, REG_MAC_CTRL);
|
||||
regs_buff[14] = AT_READ_REG(hw, REG_MAC_IPG_IFG);
|
||||
regs_buff[15] = AT_READ_REG(hw, REG_MAC_STA_ADDR);
|
||||
regs_buff[16] = AT_READ_REG(hw, REG_MAC_STA_ADDR+4);
|
||||
regs_buff[17] = AT_READ_REG(hw, REG_RX_HASH_TABLE);
|
||||
regs_buff[18] = AT_READ_REG(hw, REG_RX_HASH_TABLE+4);
|
||||
regs_buff[19] = AT_READ_REG(hw, REG_MAC_HALF_DUPLX_CTRL);
|
||||
regs_buff[20] = AT_READ_REG(hw, REG_MTU);
|
||||
regs_buff[21] = AT_READ_REG(hw, REG_WOL_CTRL);
|
||||
regs_buff[22] = AT_READ_REG(hw, REG_SRAM_TRD_ADDR);
|
||||
regs_buff[23] = AT_READ_REG(hw, REG_SRAM_TRD_LEN);
|
||||
regs_buff[24] = AT_READ_REG(hw, REG_SRAM_RXF_ADDR);
|
||||
regs_buff[25] = AT_READ_REG(hw, REG_SRAM_RXF_LEN);
|
||||
regs_buff[26] = AT_READ_REG(hw, REG_SRAM_TXF_ADDR);
|
||||
regs_buff[27] = AT_READ_REG(hw, REG_SRAM_TXF_LEN);
|
||||
regs_buff[28] = AT_READ_REG(hw, REG_SRAM_TCPH_ADDR);
|
||||
regs_buff[29] = AT_READ_REG(hw, REG_SRAM_PKTH_ADDR);
|
||||
|
||||
atl1e_read_phy_reg(hw, MII_BMCR, &phy_data);
|
||||
regs_buff[73] = (u32)phy_data;
|
||||
atl1e_read_phy_reg(hw, MII_BMSR, &phy_data);
|
||||
regs_buff[74] = (u32)phy_data;
|
||||
}
|
||||
|
||||
static int atl1e_get_eeprom_len(struct net_device *netdev)
|
||||
{
|
||||
struct atl1e_adapter *adapter = netdev_priv(netdev);
|
||||
|
||||
if (!atl1e_check_eeprom_exist(&adapter->hw))
|
||||
return AT_EEPROM_LEN;
|
||||
else
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int atl1e_get_eeprom(struct net_device *netdev,
|
||||
struct ethtool_eeprom *eeprom, u8 *bytes)
|
||||
{
|
||||
struct atl1e_adapter *adapter = netdev_priv(netdev);
|
||||
struct atl1e_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 (atl1e_check_eeprom_exist(hw)) /* not exist */
|
||||
return -EINVAL;
|
||||
|
||||
eeprom->magic = hw->vendor_id | (hw->device_id << 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 (!atl1e_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;
|
||||
}
|
||||
|
||||
static int atl1e_set_eeprom(struct net_device *netdev,
|
||||
struct ethtool_eeprom *eeprom, u8 *bytes)
|
||||
{
|
||||
struct atl1e_adapter *adapter = netdev_priv(netdev);
|
||||
struct atl1e_hw *hw = &adapter->hw;
|
||||
u32 *eeprom_buff;
|
||||
u32 *ptr;
|
||||
int first_dword, last_dword;
|
||||
int ret_val = 0;
|
||||
int i;
|
||||
|
||||
if (eeprom->len == 0)
|
||||
return -EOPNOTSUPP;
|
||||
|
||||
if (eeprom->magic != (hw->vendor_id | (hw->device_id << 16)))
|
||||
return -EINVAL;
|
||||
|
||||
first_dword = eeprom->offset >> 2;
|
||||
last_dword = (eeprom->offset + eeprom->len - 1) >> 2;
|
||||
eeprom_buff = kmalloc(AT_EEPROM_LEN, GFP_KERNEL);
|
||||
if (eeprom_buff == NULL)
|
||||
return -ENOMEM;
|
||||
|
||||
ptr = eeprom_buff;
|
||||
|
||||
if (eeprom->offset & 3) {
|
||||
/* need read/modify/write of first changed EEPROM word */
|
||||
/* only the second byte of the word is being modified */
|
||||
if (!atl1e_read_eeprom(hw, first_dword * 4, &(eeprom_buff[0]))) {
|
||||
ret_val = -EIO;
|
||||
goto out;
|
||||
}
|
||||
ptr++;
|
||||
}
|
||||
if (((eeprom->offset + eeprom->len) & 3)) {
|
||||
/* need read/modify/write of last changed EEPROM word */
|
||||
/* only the first byte of the word is being modified */
|
||||
|
||||
if (!atl1e_read_eeprom(hw, last_dword * 4,
|
||||
&(eeprom_buff[last_dword - first_dword]))) {
|
||||
ret_val = -EIO;
|
||||
goto out;
|
||||
}
|
||||
}
|
||||
|
||||
/* Device's eeprom is always little-endian, word addressable */
|
||||
memcpy(ptr, bytes, eeprom->len);
|
||||
|
||||
for (i = 0; i < last_dword - first_dword + 1; i++) {
|
||||
if (!atl1e_write_eeprom(hw, ((first_dword + i) * 4),
|
||||
eeprom_buff[i])) {
|
||||
ret_val = -EIO;
|
||||
goto out;
|
||||
}
|
||||
}
|
||||
out:
|
||||
kfree(eeprom_buff);
|
||||
return ret_val;
|
||||
}
|
||||
|
||||
static void atl1e_get_drvinfo(struct net_device *netdev,
|
||||
struct ethtool_drvinfo *drvinfo)
|
||||
{
|
||||
struct atl1e_adapter *adapter = netdev_priv(netdev);
|
||||
|
||||
strlcpy(drvinfo->driver, atl1e_driver_name, sizeof(drvinfo->driver));
|
||||
strlcpy(drvinfo->version, atl1e_driver_version,
|
||||
sizeof(drvinfo->version));
|
||||
strlcpy(drvinfo->fw_version, "L1e", sizeof(drvinfo->fw_version));
|
||||
strlcpy(drvinfo->bus_info, pci_name(adapter->pdev),
|
||||
sizeof(drvinfo->bus_info));
|
||||
drvinfo->n_stats = 0;
|
||||
drvinfo->testinfo_len = 0;
|
||||
drvinfo->regdump_len = atl1e_get_regs_len(netdev);
|
||||
drvinfo->eedump_len = atl1e_get_eeprom_len(netdev);
|
||||
}
|
||||
|
||||
static void atl1e_get_wol(struct net_device *netdev,
|
||||
struct ethtool_wolinfo *wol)
|
||||
{
|
||||
struct atl1e_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 atl1e_set_wol(struct net_device *netdev, struct ethtool_wolinfo *wol)
|
||||
{
|
||||
struct atl1e_adapter *adapter = netdev_priv(netdev);
|
||||
|
||||
if (wol->wolopts & (WAKE_ARP | WAKE_MAGICSECURE |
|
||||
WAKE_UCAST | WAKE_MCAST | WAKE_BCAST))
|
||||
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 atl1e_nway_reset(struct net_device *netdev)
|
||||
{
|
||||
struct atl1e_adapter *adapter = netdev_priv(netdev);
|
||||
if (netif_running(netdev))
|
||||
atl1e_reinit_locked(adapter);
|
||||
return 0;
|
||||
}
|
||||
|
||||
static const struct ethtool_ops atl1e_ethtool_ops = {
|
||||
.get_settings = atl1e_get_settings,
|
||||
.set_settings = atl1e_set_settings,
|
||||
.get_drvinfo = atl1e_get_drvinfo,
|
||||
.get_regs_len = atl1e_get_regs_len,
|
||||
.get_regs = atl1e_get_regs,
|
||||
.get_wol = atl1e_get_wol,
|
||||
.set_wol = atl1e_set_wol,
|
||||
.get_msglevel = atl1e_get_msglevel,
|
||||
.nway_reset = atl1e_nway_reset,
|
||||
.get_link = ethtool_op_get_link,
|
||||
.get_eeprom_len = atl1e_get_eeprom_len,
|
||||
.get_eeprom = atl1e_get_eeprom,
|
||||
.set_eeprom = atl1e_set_eeprom,
|
||||
};
|
||||
|
||||
void atl1e_set_ethtool_ops(struct net_device *netdev)
|
||||
{
|
||||
SET_ETHTOOL_OPS(netdev, &atl1e_ethtool_ops);
|
||||
}
|
651
addons/atl1e/src/3.10.108/atl1e_hw.c
Normal file
651
addons/atl1e/src/3.10.108/atl1e_hw.c
Normal file
@ -0,0 +1,651 @@
|
||||
/*
|
||||
* 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 "atl1e.h"
|
||||
|
||||
/*
|
||||
* check_eeprom_exist
|
||||
* return 0 if eeprom exist
|
||||
*/
|
||||
int atl1e_check_eeprom_exist(struct atl1e_hw *hw)
|
||||
{
|
||||
u32 value;
|
||||
|
||||
value = AT_READ_REG(hw, REG_SPI_FLASH_CTRL);
|
||||
if (value & SPI_FLASH_CTRL_EN_VPD) {
|
||||
value &= ~SPI_FLASH_CTRL_EN_VPD;
|
||||
AT_WRITE_REG(hw, REG_SPI_FLASH_CTRL, value);
|
||||
}
|
||||
value = AT_READ_REGW(hw, REG_PCIE_CAP_LIST);
|
||||
return ((value & 0xFF00) == 0x6C00) ? 0 : 1;
|
||||
}
|
||||
|
||||
void atl1e_hw_set_mac_addr(struct atl1e_hw *hw)
|
||||
{
|
||||
u32 value;
|
||||
/*
|
||||
* 00-0B-6A-F6-00-DC
|
||||
* 0: 6AF600DC 1: 000B
|
||||
* low dword
|
||||
*/
|
||||
value = (((u32)hw->mac_addr[2]) << 24) |
|
||||
(((u32)hw->mac_addr[3]) << 16) |
|
||||
(((u32)hw->mac_addr[4]) << 8) |
|
||||
(((u32)hw->mac_addr[5])) ;
|
||||
AT_WRITE_REG_ARRAY(hw, REG_MAC_STA_ADDR, 0, value);
|
||||
/* hight dword */
|
||||
value = (((u32)hw->mac_addr[0]) << 8) |
|
||||
(((u32)hw->mac_addr[1])) ;
|
||||
AT_WRITE_REG_ARRAY(hw, REG_MAC_STA_ADDR, 1, value);
|
||||
}
|
||||
|
||||
/*
|
||||
* atl1e_get_permanent_address
|
||||
* return 0 if get valid mac address,
|
||||
*/
|
||||
static int atl1e_get_permanent_address(struct atl1e_hw *hw)
|
||||
{
|
||||
u32 addr[2];
|
||||
u32 i;
|
||||
u32 twsi_ctrl_data;
|
||||
u8 eth_addr[ETH_ALEN];
|
||||
|
||||
if (is_valid_ether_addr(hw->perm_mac_addr))
|
||||
return 0;
|
||||
|
||||
/* init */
|
||||
addr[0] = addr[1] = 0;
|
||||
|
||||
if (!atl1e_check_eeprom_exist(hw)) {
|
||||
/* eeprom exist */
|
||||
twsi_ctrl_data = AT_READ_REG(hw, REG_TWSI_CTRL);
|
||||
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);
|
||||
twsi_ctrl_data = AT_READ_REG(hw, REG_TWSI_CTRL);
|
||||
if ((twsi_ctrl_data & TWSI_CTRL_SW_LDSTART) == 0)
|
||||
break;
|
||||
}
|
||||
if (i >= AT_TWSI_EEPROM_TIMEOUT)
|
||||
return AT_ERR_TIMEOUT;
|
||||
}
|
||||
|
||||
/* maybe MAC-address is from BIOS */
|
||||
addr[0] = AT_READ_REG(hw, REG_MAC_STA_ADDR);
|
||||
addr[1] = AT_READ_REG(hw, REG_MAC_STA_ADDR + 4);
|
||||
*(u32 *) ð_addr[2] = swab32(addr[0]);
|
||||
*(u16 *) ð_addr[0] = swab16(*(u16 *)&addr[1]);
|
||||
|
||||
if (is_valid_ether_addr(eth_addr)) {
|
||||
memcpy(hw->perm_mac_addr, eth_addr, ETH_ALEN);
|
||||
return 0;
|
||||
}
|
||||
|
||||
return AT_ERR_EEPROM;
|
||||
}
|
||||
|
||||
bool atl1e_write_eeprom(struct atl1e_hw *hw, u32 offset, u32 value)
|
||||
{
|
||||
return true;
|
||||
}
|
||||
|
||||
bool atl1e_read_eeprom(struct atl1e_hw *hw, u32 offset, u32 *p_value)
|
||||
{
|
||||
int i;
|
||||
u32 control;
|
||||
|
||||
if (offset & 3)
|
||||
return false; /* address do not align */
|
||||
|
||||
AT_WRITE_REG(hw, REG_VPD_DATA, 0);
|
||||
control = (offset & VPD_CAP_VPD_ADDR_MASK) << VPD_CAP_VPD_ADDR_SHIFT;
|
||||
AT_WRITE_REG(hw, REG_VPD_CAP, control);
|
||||
|
||||
for (i = 0; i < 10; i++) {
|
||||
msleep(2);
|
||||
control = AT_READ_REG(hw, REG_VPD_CAP);
|
||||
if (control & VPD_CAP_VPD_FLAG)
|
||||
break;
|
||||
}
|
||||
if (control & VPD_CAP_VPD_FLAG) {
|
||||
*p_value = AT_READ_REG(hw, REG_VPD_DATA);
|
||||
return true;
|
||||
}
|
||||
return false; /* timeout */
|
||||
}
|
||||
|
||||
void atl1e_force_ps(struct atl1e_hw *hw)
|
||||
{
|
||||
AT_WRITE_REGW(hw, REG_GPHY_CTRL,
|
||||
GPHY_CTRL_PW_WOL_DIS | GPHY_CTRL_EXT_RESET);
|
||||
}
|
||||
|
||||
/*
|
||||
* Reads the adapter's MAC address from the EEPROM
|
||||
*
|
||||
* hw - Struct containing variables accessed by shared code
|
||||
*/
|
||||
int atl1e_read_mac_addr(struct atl1e_hw *hw)
|
||||
{
|
||||
int err = 0;
|
||||
|
||||
err = atl1e_get_permanent_address(hw);
|
||||
if (err)
|
||||
return AT_ERR_EEPROM;
|
||||
memcpy(hw->mac_addr, hw->perm_mac_addr, sizeof(hw->perm_mac_addr));
|
||||
return 0;
|
||||
}
|
||||
|
||||
/*
|
||||
* atl1e_hash_mc_addr
|
||||
* purpose
|
||||
* set hash value for a multicast address
|
||||
*/
|
||||
u32 atl1e_hash_mc_addr(struct atl1e_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 atl1e_hash_set(struct atl1e_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 7 bits 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);
|
||||
}
|
||||
/*
|
||||
* 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 atl1e_read_phy_reg(struct atl1e_hw *hw, u16 reg_addr, u16 *phy_data)
|
||||
{
|
||||
u32 val;
|
||||
int i;
|
||||
|
||||
val = ((u32)(reg_addr & MDIO_REG_ADDR_MASK)) << MDIO_REG_ADDR_SHIFT |
|
||||
MDIO_START | MDIO_SUP_PREAMBLE | MDIO_RW |
|
||||
MDIO_CLK_25_4 << MDIO_CLK_SEL_SHIFT;
|
||||
|
||||
AT_WRITE_REG(hw, REG_MDIO_CTRL, val);
|
||||
|
||||
wmb();
|
||||
|
||||
for (i = 0; i < MDIO_WAIT_TIMES; i++) {
|
||||
udelay(2);
|
||||
val = AT_READ_REG(hw, REG_MDIO_CTRL);
|
||||
if (!(val & (MDIO_START | MDIO_BUSY)))
|
||||
break;
|
||||
wmb();
|
||||
}
|
||||
if (!(val & (MDIO_START | MDIO_BUSY))) {
|
||||
*phy_data = (u16)val;
|
||||
return 0;
|
||||
}
|
||||
|
||||
return AT_ERR_PHY;
|
||||
}
|
||||
|
||||
/*
|
||||
* 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 atl1e_write_phy_reg(struct atl1e_hw *hw, u32 reg_addr, u16 phy_data)
|
||||
{
|
||||
int i;
|
||||
u32 val;
|
||||
|
||||
val = ((u32)(phy_data & MDIO_DATA_MASK)) << MDIO_DATA_SHIFT |
|
||||
(reg_addr&MDIO_REG_ADDR_MASK) << MDIO_REG_ADDR_SHIFT |
|
||||
MDIO_SUP_PREAMBLE |
|
||||
MDIO_START |
|
||||
MDIO_CLK_25_4 << MDIO_CLK_SEL_SHIFT;
|
||||
|
||||
AT_WRITE_REG(hw, REG_MDIO_CTRL, val);
|
||||
wmb();
|
||||
|
||||
for (i = 0; i < MDIO_WAIT_TIMES; i++) {
|
||||
udelay(2);
|
||||
val = AT_READ_REG(hw, REG_MDIO_CTRL);
|
||||
if (!(val & (MDIO_START | MDIO_BUSY)))
|
||||
break;
|
||||
wmb();
|
||||
}
|
||||
|
||||
if (!(val & (MDIO_START | MDIO_BUSY)))
|
||||
return 0;
|
||||
|
||||
return AT_ERR_PHY;
|
||||
}
|
||||
|
||||
/*
|
||||
* atl1e_init_pcie - init PCIE module
|
||||
*/
|
||||
static void atl1e_init_pcie(struct atl1e_hw *hw)
|
||||
{
|
||||
u32 value;
|
||||
/* comment 2lines below to save more power when sususpend
|
||||
value = LTSSM_TEST_MODE_DEF;
|
||||
AT_WRITE_REG(hw, REG_LTSSM_TEST_MODE, value);
|
||||
*/
|
||||
|
||||
/* pcie flow control mode change */
|
||||
value = AT_READ_REG(hw, 0x1008);
|
||||
value |= 0x8000;
|
||||
AT_WRITE_REG(hw, 0x1008, value);
|
||||
}
|
||||
/*
|
||||
* Configures PHY autoneg and flow control advertisement settings
|
||||
*
|
||||
* hw - Struct containing variables accessed by shared code
|
||||
*/
|
||||
static int atl1e_phy_setup_autoneg_adv(struct atl1e_hw *hw)
|
||||
{
|
||||
s32 ret_val;
|
||||
u16 mii_autoneg_adv_reg;
|
||||
u16 mii_1000t_ctrl_reg;
|
||||
|
||||
if (0 != hw->mii_autoneg_adv_reg)
|
||||
return 0;
|
||||
/* Read the MII Auto-Neg Advertisement Register (Address 4/9). */
|
||||
mii_autoneg_adv_reg = MII_AR_DEFAULT_CAP_MASK;
|
||||
mii_1000t_ctrl_reg = MII_AT001_CR_1000T_DEFAULT_CAP_MASK;
|
||||
|
||||
/*
|
||||
* Need to parse autoneg_advertised and set up
|
||||
* the appropriate PHY registers. First we will parse for
|
||||
* autoneg_advertised software override. Since we can advertise
|
||||
* a plethora of combinations, we need to check each bit
|
||||
* individually.
|
||||
*/
|
||||
|
||||
/*
|
||||
* First we clear all the 10/100 mb speed bits in the Auto-Neg
|
||||
* Advertisement Register (Address 4) and the 1000 mb speed bits in
|
||||
* the 1000Base-T control Register (Address 9).
|
||||
*/
|
||||
mii_autoneg_adv_reg &= ~ADVERTISE_ALL;
|
||||
mii_1000t_ctrl_reg &= ~MII_AT001_CR_1000T_SPEED_MASK;
|
||||
|
||||
/*
|
||||
* Need to parse MediaType and setup the
|
||||
* appropriate PHY registers.
|
||||
*/
|
||||
switch (hw->media_type) {
|
||||
case MEDIA_TYPE_AUTO_SENSOR:
|
||||
mii_autoneg_adv_reg |= ADVERTISE_ALL;
|
||||
hw->autoneg_advertised = ADVERTISE_ALL;
|
||||
if (hw->nic_type == athr_l1e) {
|
||||
mii_1000t_ctrl_reg |= ADVERTISE_1000FULL;
|
||||
hw->autoneg_advertised |= ADVERTISE_1000_FULL;
|
||||
}
|
||||
break;
|
||||
|
||||
case MEDIA_TYPE_100M_FULL:
|
||||
mii_autoneg_adv_reg |= ADVERTISE_100FULL;
|
||||
hw->autoneg_advertised = ADVERTISE_100_FULL;
|
||||
break;
|
||||
|
||||
case MEDIA_TYPE_100M_HALF:
|
||||
mii_autoneg_adv_reg |= ADVERTISE_100_HALF;
|
||||
hw->autoneg_advertised = ADVERTISE_100_HALF;
|
||||
break;
|
||||
|
||||
case MEDIA_TYPE_10M_FULL:
|
||||
mii_autoneg_adv_reg |= ADVERTISE_10_FULL;
|
||||
hw->autoneg_advertised = ADVERTISE_10_FULL;
|
||||
break;
|
||||
|
||||
default:
|
||||
mii_autoneg_adv_reg |= ADVERTISE_10_HALF;
|
||||
hw->autoneg_advertised = ADVERTISE_10_HALF;
|
||||
break;
|
||||
}
|
||||
|
||||
/* flow control fixed to enable all */
|
||||
mii_autoneg_adv_reg |= (ADVERTISE_PAUSE_ASYM | ADVERTISE_PAUSE_CAP);
|
||||
|
||||
hw->mii_autoneg_adv_reg = mii_autoneg_adv_reg;
|
||||
hw->mii_1000t_ctrl_reg = mii_1000t_ctrl_reg;
|
||||
|
||||
ret_val = atl1e_write_phy_reg(hw, MII_ADVERTISE, mii_autoneg_adv_reg);
|
||||
if (ret_val)
|
||||
return ret_val;
|
||||
|
||||
if (hw->nic_type == athr_l1e || hw->nic_type == athr_l2e_revA) {
|
||||
ret_val = atl1e_write_phy_reg(hw, MII_CTRL1000,
|
||||
mii_1000t_ctrl_reg);
|
||||
if (ret_val)
|
||||
return ret_val;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
* Resets the PHY and make all config validate
|
||||
*
|
||||
* hw - Struct containing variables accessed by shared code
|
||||
*
|
||||
* Sets bit 15 and 12 of the MII control regiser (for F001 bug)
|
||||
*/
|
||||
int atl1e_phy_commit(struct atl1e_hw *hw)
|
||||
{
|
||||
struct atl1e_adapter *adapter = hw->adapter;
|
||||
int ret_val;
|
||||
u16 phy_data;
|
||||
|
||||
phy_data = BMCR_RESET | BMCR_ANENABLE | BMCR_ANRESTART;
|
||||
|
||||
ret_val = atl1e_write_phy_reg(hw, MII_BMCR, phy_data);
|
||||
if (ret_val) {
|
||||
u32 val;
|
||||
int i;
|
||||
/**************************************
|
||||
* pcie serdes link may be down !
|
||||
**************************************/
|
||||
for (i = 0; i < 25; i++) {
|
||||
msleep(1);
|
||||
val = AT_READ_REG(hw, REG_MDIO_CTRL);
|
||||
if (!(val & (MDIO_START | MDIO_BUSY)))
|
||||
break;
|
||||
}
|
||||
|
||||
if (0 != (val & (MDIO_START | MDIO_BUSY))) {
|
||||
netdev_err(adapter->netdev,
|
||||
"pcie linkdown at least for 25ms\n");
|
||||
return ret_val;
|
||||
}
|
||||
|
||||
netdev_err(adapter->netdev, "pcie linkup after %d ms\n", i);
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
int atl1e_phy_init(struct atl1e_hw *hw)
|
||||
{
|
||||
struct atl1e_adapter *adapter = hw->adapter;
|
||||
s32 ret_val;
|
||||
u16 phy_val;
|
||||
|
||||
if (hw->phy_configured) {
|
||||
if (hw->re_autoneg) {
|
||||
hw->re_autoneg = false;
|
||||
return atl1e_restart_autoneg(hw);
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* RESET GPHY Core */
|
||||
AT_WRITE_REGW(hw, REG_GPHY_CTRL, GPHY_CTRL_DEFAULT);
|
||||
msleep(2);
|
||||
AT_WRITE_REGW(hw, REG_GPHY_CTRL, GPHY_CTRL_DEFAULT |
|
||||
GPHY_CTRL_EXT_RESET);
|
||||
msleep(2);
|
||||
|
||||
/* patches */
|
||||
/* p1. eable hibernation mode */
|
||||
ret_val = atl1e_write_phy_reg(hw, MII_DBG_ADDR, 0xB);
|
||||
if (ret_val)
|
||||
return ret_val;
|
||||
ret_val = atl1e_write_phy_reg(hw, MII_DBG_DATA, 0xBC00);
|
||||
if (ret_val)
|
||||
return ret_val;
|
||||
/* p2. set Class A/B for all modes */
|
||||
ret_val = atl1e_write_phy_reg(hw, MII_DBG_ADDR, 0);
|
||||
if (ret_val)
|
||||
return ret_val;
|
||||
phy_val = 0x02ef;
|
||||
/* remove Class AB */
|
||||
/* phy_val = hw->emi_ca ? 0x02ef : 0x02df; */
|
||||
ret_val = atl1e_write_phy_reg(hw, MII_DBG_DATA, phy_val);
|
||||
if (ret_val)
|
||||
return ret_val;
|
||||
/* p3. 10B ??? */
|
||||
ret_val = atl1e_write_phy_reg(hw, MII_DBG_ADDR, 0x12);
|
||||
if (ret_val)
|
||||
return ret_val;
|
||||
ret_val = atl1e_write_phy_reg(hw, MII_DBG_DATA, 0x4C04);
|
||||
if (ret_val)
|
||||
return ret_val;
|
||||
/* p4. 1000T power */
|
||||
ret_val = atl1e_write_phy_reg(hw, MII_DBG_ADDR, 0x4);
|
||||
if (ret_val)
|
||||
return ret_val;
|
||||
ret_val = atl1e_write_phy_reg(hw, MII_DBG_DATA, 0x8BBB);
|
||||
if (ret_val)
|
||||
return ret_val;
|
||||
|
||||
ret_val = atl1e_write_phy_reg(hw, MII_DBG_ADDR, 0x5);
|
||||
if (ret_val)
|
||||
return ret_val;
|
||||
ret_val = atl1e_write_phy_reg(hw, MII_DBG_DATA, 0x2C46);
|
||||
if (ret_val)
|
||||
return ret_val;
|
||||
|
||||
msleep(1);
|
||||
|
||||
/*Enable PHY LinkChange Interrupt */
|
||||
ret_val = atl1e_write_phy_reg(hw, MII_INT_CTRL, 0xC00);
|
||||
if (ret_val) {
|
||||
netdev_err(adapter->netdev,
|
||||
"Error enable PHY linkChange Interrupt\n");
|
||||
return ret_val;
|
||||
}
|
||||
/* setup AutoNeg parameters */
|
||||
ret_val = atl1e_phy_setup_autoneg_adv(hw);
|
||||
if (ret_val) {
|
||||
netdev_err(adapter->netdev,
|
||||
"Error Setting up Auto-Negotiation\n");
|
||||
return ret_val;
|
||||
}
|
||||
/* SW.Reset & En-Auto-Neg to restart Auto-Neg*/
|
||||
netdev_dbg(adapter->netdev, "Restarting Auto-Negotiation\n");
|
||||
ret_val = atl1e_phy_commit(hw);
|
||||
if (ret_val) {
|
||||
netdev_err(adapter->netdev, "Error resetting the phy\n");
|
||||
return ret_val;
|
||||
}
|
||||
|
||||
hw->phy_configured = true;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
/*
|
||||
* Reset the transmit and receive units; mask and clear all interrupts.
|
||||
* hw - Struct containing variables accessed by shared code
|
||||
* return : 0 or idle status (if error)
|
||||
*/
|
||||
int atl1e_reset_hw(struct atl1e_hw *hw)
|
||||
{
|
||||
struct atl1e_adapter *adapter = hw->adapter;
|
||||
struct pci_dev *pdev = adapter->pdev;
|
||||
|
||||
u32 idle_status_data = 0;
|
||||
u16 pci_cfg_cmd_word = 0;
|
||||
int timeout = 0;
|
||||
|
||||
/* Workaround for PCI problem when BIOS sets MMRBC incorrectly. */
|
||||
pci_read_config_word(pdev, PCI_REG_COMMAND, &pci_cfg_cmd_word);
|
||||
if ((pci_cfg_cmd_word & (CMD_IO_SPACE |
|
||||
CMD_MEMORY_SPACE | CMD_BUS_MASTER))
|
||||
!= (CMD_IO_SPACE | CMD_MEMORY_SPACE | CMD_BUS_MASTER)) {
|
||||
pci_cfg_cmd_word |= (CMD_IO_SPACE |
|
||||
CMD_MEMORY_SPACE | CMD_BUS_MASTER);
|
||||
pci_write_config_word(pdev, PCI_REG_COMMAND, pci_cfg_cmd_word);
|
||||
}
|
||||
|
||||
/*
|
||||
* Issue Soft Reset to the MAC. This will reset the chip's
|
||||
* transmit, receive, DMA. It will not effect
|
||||
* the current PCI configuration. The global reset bit is self-
|
||||
* clearing, and should clear within a microsecond.
|
||||
*/
|
||||
AT_WRITE_REG(hw, REG_MASTER_CTRL,
|
||||
MASTER_CTRL_LED_MODE | MASTER_CTRL_SOFT_RST);
|
||||
wmb();
|
||||
msleep(1);
|
||||
|
||||
/* Wait at least 10ms for All module to be Idle */
|
||||
for (timeout = 0; timeout < AT_HW_MAX_IDLE_DELAY; timeout++) {
|
||||
idle_status_data = AT_READ_REG(hw, REG_IDLE_STATUS);
|
||||
if (idle_status_data == 0)
|
||||
break;
|
||||
msleep(1);
|
||||
cpu_relax();
|
||||
}
|
||||
|
||||
if (timeout >= AT_HW_MAX_IDLE_DELAY) {
|
||||
netdev_err(adapter->netdev,
|
||||
"MAC state machine can't be idle since disabled for 10ms second\n");
|
||||
return AT_ERR_TIMEOUT;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
* Performs basic configuration of the adapter.
|
||||
*
|
||||
* hw - Struct containing variables accessed by shared code
|
||||
* Assumes that the controller has previously been reset and is in a
|
||||
* post-reset uninitialized state. Initializes multicast table,
|
||||
* and Calls routines to setup link
|
||||
* Leaves the transmit and receive units disabled and uninitialized.
|
||||
*/
|
||||
int atl1e_init_hw(struct atl1e_hw *hw)
|
||||
{
|
||||
s32 ret_val = 0;
|
||||
|
||||
atl1e_init_pcie(hw);
|
||||
|
||||
/* Zero out the Multicast HASH table */
|
||||
/* clear the old settings from the multicast hash table */
|
||||
AT_WRITE_REG(hw, REG_RX_HASH_TABLE, 0);
|
||||
AT_WRITE_REG_ARRAY(hw, REG_RX_HASH_TABLE, 1, 0);
|
||||
|
||||
ret_val = atl1e_phy_init(hw);
|
||||
|
||||
return ret_val;
|
||||
}
|
||||
|
||||
/*
|
||||
* 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 atl1e_get_speed_and_duplex(struct atl1e_hw *hw, u16 *speed, u16 *duplex)
|
||||
{
|
||||
int err;
|
||||
u16 phy_data;
|
||||
|
||||
/* Read PHY Specific Status Register (17) */
|
||||
err = atl1e_read_phy_reg(hw, MII_AT001_PSSR, &phy_data);
|
||||
if (err)
|
||||
return err;
|
||||
|
||||
if (!(phy_data & MII_AT001_PSSR_SPD_DPLX_RESOLVED))
|
||||
return AT_ERR_PHY_RES;
|
||||
|
||||
switch (phy_data & MII_AT001_PSSR_SPEED) {
|
||||
case MII_AT001_PSSR_1000MBS:
|
||||
*speed = SPEED_1000;
|
||||
break;
|
||||
case MII_AT001_PSSR_100MBS:
|
||||
*speed = SPEED_100;
|
||||
break;
|
||||
case MII_AT001_PSSR_10MBS:
|
||||
*speed = SPEED_10;
|
||||
break;
|
||||
default:
|
||||
return AT_ERR_PHY_SPEED;
|
||||
break;
|
||||
}
|
||||
|
||||
if (phy_data & MII_AT001_PSSR_DPLX)
|
||||
*duplex = FULL_DUPLEX;
|
||||
else
|
||||
*duplex = HALF_DUPLEX;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
int atl1e_restart_autoneg(struct atl1e_hw *hw)
|
||||
{
|
||||
int err = 0;
|
||||
|
||||
err = atl1e_write_phy_reg(hw, MII_ADVERTISE, hw->mii_autoneg_adv_reg);
|
||||
if (err)
|
||||
return err;
|
||||
|
||||
if (hw->nic_type == athr_l1e || hw->nic_type == athr_l2e_revA) {
|
||||
err = atl1e_write_phy_reg(hw, MII_CTRL1000,
|
||||
hw->mii_1000t_ctrl_reg);
|
||||
if (err)
|
||||
return err;
|
||||
}
|
||||
|
||||
err = atl1e_write_phy_reg(hw, MII_BMCR,
|
||||
BMCR_RESET | BMCR_ANENABLE | BMCR_ANRESTART);
|
||||
return err;
|
||||
}
|
||||
|
690
addons/atl1e/src/3.10.108/atl1e_hw.h
Normal file
690
addons/atl1e/src/3.10.108/atl1e_hw.h
Normal file
@ -0,0 +1,690 @@
|
||||
/*
|
||||
* 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.
|
||||
*/
|
||||
|
||||
#ifndef _ATHL1E_HW_H_
|
||||
#define _ATHL1E_HW_H_
|
||||
|
||||
#include <linux/types.h>
|
||||
#include <linux/mii.h>
|
||||
|
||||
struct atl1e_adapter;
|
||||
struct atl1e_hw;
|
||||
|
||||
/* function prototype */
|
||||
s32 atl1e_reset_hw(struct atl1e_hw *hw);
|
||||
s32 atl1e_read_mac_addr(struct atl1e_hw *hw);
|
||||
s32 atl1e_init_hw(struct atl1e_hw *hw);
|
||||
s32 atl1e_phy_commit(struct atl1e_hw *hw);
|
||||
s32 atl1e_get_speed_and_duplex(struct atl1e_hw *hw, u16 *speed, u16 *duplex);
|
||||
u32 atl1e_auto_get_fc(struct atl1e_adapter *adapter, u16 duplex);
|
||||
u32 atl1e_hash_mc_addr(struct atl1e_hw *hw, u8 *mc_addr);
|
||||
void atl1e_hash_set(struct atl1e_hw *hw, u32 hash_value);
|
||||
s32 atl1e_read_phy_reg(struct atl1e_hw *hw, u16 reg_addr, u16 *phy_data);
|
||||
s32 atl1e_write_phy_reg(struct atl1e_hw *hw, u32 reg_addr, u16 phy_data);
|
||||
s32 atl1e_validate_mdi_setting(struct atl1e_hw *hw);
|
||||
void atl1e_hw_set_mac_addr(struct atl1e_hw *hw);
|
||||
bool atl1e_read_eeprom(struct atl1e_hw *hw, u32 offset, u32 *p_value);
|
||||
bool atl1e_write_eeprom(struct atl1e_hw *hw, u32 offset, u32 value);
|
||||
s32 atl1e_phy_enter_power_saving(struct atl1e_hw *hw);
|
||||
s32 atl1e_phy_leave_power_saving(struct atl1e_hw *hw);
|
||||
s32 atl1e_phy_init(struct atl1e_hw *hw);
|
||||
int atl1e_check_eeprom_exist(struct atl1e_hw *hw);
|
||||
void atl1e_force_ps(struct atl1e_hw *hw);
|
||||
s32 atl1e_restart_autoneg(struct atl1e_hw *hw);
|
||||
|
||||
/* register definition */
|
||||
#define REG_PM_CTRLSTAT 0x44
|
||||
|
||||
#define REG_PCIE_CAP_LIST 0x58
|
||||
|
||||
#define REG_DEVICE_CAP 0x5C
|
||||
#define DEVICE_CAP_MAX_PAYLOAD_MASK 0x7
|
||||
#define DEVICE_CAP_MAX_PAYLOAD_SHIFT 0
|
||||
|
||||
#define REG_DEVICE_CTRL 0x60
|
||||
#define DEVICE_CTRL_MAX_PAYLOAD_MASK 0x7
|
||||
#define DEVICE_CTRL_MAX_PAYLOAD_SHIFT 5
|
||||
#define DEVICE_CTRL_MAX_RREQ_SZ_MASK 0x7
|
||||
#define DEVICE_CTRL_MAX_RREQ_SZ_SHIFT 12
|
||||
|
||||
#define REG_VPD_CAP 0x6C
|
||||
#define VPD_CAP_ID_MASK 0xff
|
||||
#define VPD_CAP_ID_SHIFT 0
|
||||
#define VPD_CAP_NEXT_PTR_MASK 0xFF
|
||||
#define VPD_CAP_NEXT_PTR_SHIFT 8
|
||||
#define VPD_CAP_VPD_ADDR_MASK 0x7FFF
|
||||
#define VPD_CAP_VPD_ADDR_SHIFT 16
|
||||
#define VPD_CAP_VPD_FLAG 0x80000000
|
||||
|
||||
#define REG_VPD_DATA 0x70
|
||||
|
||||
#define REG_SPI_FLASH_CTRL 0x200
|
||||
#define SPI_FLASH_CTRL_STS_NON_RDY 0x1
|
||||
#define SPI_FLASH_CTRL_STS_WEN 0x2
|
||||
#define SPI_FLASH_CTRL_STS_WPEN 0x80
|
||||
#define SPI_FLASH_CTRL_DEV_STS_MASK 0xFF
|
||||
#define SPI_FLASH_CTRL_DEV_STS_SHIFT 0
|
||||
#define SPI_FLASH_CTRL_INS_MASK 0x7
|
||||
#define SPI_FLASH_CTRL_INS_SHIFT 8
|
||||
#define SPI_FLASH_CTRL_START 0x800
|
||||
#define SPI_FLASH_CTRL_EN_VPD 0x2000
|
||||
#define SPI_FLASH_CTRL_LDSTART 0x8000
|
||||
#define SPI_FLASH_CTRL_CS_HI_MASK 0x3
|
||||
#define SPI_FLASH_CTRL_CS_HI_SHIFT 16
|
||||
#define SPI_FLASH_CTRL_CS_HOLD_MASK 0x3
|
||||
#define SPI_FLASH_CTRL_CS_HOLD_SHIFT 18
|
||||
#define SPI_FLASH_CTRL_CLK_LO_MASK 0x3
|
||||
#define SPI_FLASH_CTRL_CLK_LO_SHIFT 20
|
||||
#define SPI_FLASH_CTRL_CLK_HI_MASK 0x3
|
||||
#define SPI_FLASH_CTRL_CLK_HI_SHIFT 22
|
||||
#define SPI_FLASH_CTRL_CS_SETUP_MASK 0x3
|
||||
#define SPI_FLASH_CTRL_CS_SETUP_SHIFT 24
|
||||
#define SPI_FLASH_CTRL_EROM_PGSZ_MASK 0x3
|
||||
#define SPI_FLASH_CTRL_EROM_PGSZ_SHIFT 26
|
||||
#define SPI_FLASH_CTRL_WAIT_READY 0x10000000
|
||||
|
||||
#define REG_SPI_ADDR 0x204
|
||||
|
||||
#define REG_SPI_DATA 0x208
|
||||
|
||||
#define REG_SPI_FLASH_CONFIG 0x20C
|
||||
#define SPI_FLASH_CONFIG_LD_ADDR_MASK 0xFFFFFF
|
||||
#define SPI_FLASH_CONFIG_LD_ADDR_SHIFT 0
|
||||
#define SPI_FLASH_CONFIG_VPD_ADDR_MASK 0x3
|
||||
#define SPI_FLASH_CONFIG_VPD_ADDR_SHIFT 24
|
||||
#define SPI_FLASH_CONFIG_LD_EXIST 0x4000000
|
||||
|
||||
|
||||
#define REG_SPI_FLASH_OP_PROGRAM 0x210
|
||||
#define REG_SPI_FLASH_OP_SC_ERASE 0x211
|
||||
#define REG_SPI_FLASH_OP_CHIP_ERASE 0x212
|
||||
#define REG_SPI_FLASH_OP_RDID 0x213
|
||||
#define REG_SPI_FLASH_OP_WREN 0x214
|
||||
#define REG_SPI_FLASH_OP_RDSR 0x215
|
||||
#define REG_SPI_FLASH_OP_WRSR 0x216
|
||||
#define REG_SPI_FLASH_OP_READ 0x217
|
||||
|
||||
#define REG_TWSI_CTRL 0x218
|
||||
#define TWSI_CTRL_LD_OFFSET_MASK 0xFF
|
||||
#define TWSI_CTRL_LD_OFFSET_SHIFT 0
|
||||
#define TWSI_CTRL_LD_SLV_ADDR_MASK 0x7
|
||||
#define TWSI_CTRL_LD_SLV_ADDR_SHIFT 8
|
||||
#define TWSI_CTRL_SW_LDSTART 0x800
|
||||
#define TWSI_CTRL_HW_LDSTART 0x1000
|
||||
#define TWSI_CTRL_SMB_SLV_ADDR_MASK 0x0x7F
|
||||
#define TWSI_CTRL_SMB_SLV_ADDR_SHIFT 15
|
||||
#define TWSI_CTRL_LD_EXIST 0x400000
|
||||
#define TWSI_CTRL_READ_FREQ_SEL_MASK 0x3
|
||||
#define TWSI_CTRL_READ_FREQ_SEL_SHIFT 23
|
||||
#define TWSI_CTRL_FREQ_SEL_100K 0
|
||||
#define TWSI_CTRL_FREQ_SEL_200K 1
|
||||
#define TWSI_CTRL_FREQ_SEL_300K 2
|
||||
#define TWSI_CTRL_FREQ_SEL_400K 3
|
||||
#define TWSI_CTRL_SMB_SLV_ADDR
|
||||
#define TWSI_CTRL_WRITE_FREQ_SEL_MASK 0x3
|
||||
#define TWSI_CTRL_WRITE_FREQ_SEL_SHIFT 24
|
||||
|
||||
|
||||
#define REG_PCIE_DEV_MISC_CTRL 0x21C
|
||||
#define PCIE_DEV_MISC_CTRL_EXT_PIPE 0x2
|
||||
#define PCIE_DEV_MISC_CTRL_RETRY_BUFDIS 0x1
|
||||
#define PCIE_DEV_MISC_CTRL_SPIROM_EXIST 0x4
|
||||
#define PCIE_DEV_MISC_CTRL_SERDES_ENDIAN 0x8
|
||||
#define PCIE_DEV_MISC_CTRL_SERDES_SEL_DIN 0x10
|
||||
|
||||
#define REG_PCIE_PHYMISC 0x1000
|
||||
#define PCIE_PHYMISC_FORCE_RCV_DET 0x4
|
||||
|
||||
#define REG_LTSSM_TEST_MODE 0x12FC
|
||||
#define LTSSM_TEST_MODE_DEF 0xE000
|
||||
|
||||
/* Selene Master Control Register */
|
||||
#define REG_MASTER_CTRL 0x1400
|
||||
#define MASTER_CTRL_SOFT_RST 0x1
|
||||
#define MASTER_CTRL_MTIMER_EN 0x2
|
||||
#define MASTER_CTRL_ITIMER_EN 0x4
|
||||
#define MASTER_CTRL_MANUAL_INT 0x8
|
||||
#define MASTER_CTRL_ITIMER2_EN 0x20
|
||||
#define MASTER_CTRL_INT_RDCLR 0x40
|
||||
#define MASTER_CTRL_LED_MODE 0x200
|
||||
#define MASTER_CTRL_REV_NUM_SHIFT 16
|
||||
#define MASTER_CTRL_REV_NUM_MASK 0xff
|
||||
#define MASTER_CTRL_DEV_ID_SHIFT 24
|
||||
#define MASTER_CTRL_DEV_ID_MASK 0xff
|
||||
|
||||
/* Timer Initial Value Register */
|
||||
#define REG_MANUAL_TIMER_INIT 0x1404
|
||||
|
||||
|
||||
/* IRQ ModeratorTimer Initial Value Register */
|
||||
#define REG_IRQ_MODU_TIMER_INIT 0x1408 /* w */
|
||||
#define REG_IRQ_MODU_TIMER2_INIT 0x140A /* w */
|
||||
|
||||
|
||||
#define REG_GPHY_CTRL 0x140C
|
||||
#define GPHY_CTRL_EXT_RESET 1
|
||||
#define GPHY_CTRL_PIPE_MOD 2
|
||||
#define GPHY_CTRL_TEST_MODE_MASK 3
|
||||
#define GPHY_CTRL_TEST_MODE_SHIFT 2
|
||||
#define GPHY_CTRL_BERT_START 0x10
|
||||
#define GPHY_CTRL_GATE_25M_EN 0x20
|
||||
#define GPHY_CTRL_LPW_EXIT 0x40
|
||||
#define GPHY_CTRL_PHY_IDDQ 0x80
|
||||
#define GPHY_CTRL_PHY_IDDQ_DIS 0x100
|
||||
#define GPHY_CTRL_PCLK_SEL_DIS 0x200
|
||||
#define GPHY_CTRL_HIB_EN 0x400
|
||||
#define GPHY_CTRL_HIB_PULSE 0x800
|
||||
#define GPHY_CTRL_SEL_ANA_RST 0x1000
|
||||
#define GPHY_CTRL_PHY_PLL_ON 0x2000
|
||||
#define GPHY_CTRL_PWDOWN_HW 0x4000
|
||||
#define GPHY_CTRL_DEFAULT (\
|
||||
GPHY_CTRL_PHY_PLL_ON |\
|
||||
GPHY_CTRL_SEL_ANA_RST |\
|
||||
GPHY_CTRL_HIB_PULSE |\
|
||||
GPHY_CTRL_HIB_EN)
|
||||
|
||||
#define GPHY_CTRL_PW_WOL_DIS (\
|
||||
GPHY_CTRL_PHY_PLL_ON |\
|
||||
GPHY_CTRL_SEL_ANA_RST |\
|
||||
GPHY_CTRL_HIB_PULSE |\
|
||||
GPHY_CTRL_HIB_EN |\
|
||||
GPHY_CTRL_PWDOWN_HW |\
|
||||
GPHY_CTRL_PCLK_SEL_DIS |\
|
||||
GPHY_CTRL_PHY_IDDQ)
|
||||
|
||||
/* IRQ Anti-Lost Timer Initial Value Register */
|
||||
#define REG_CMBDISDMA_TIMER 0x140E
|
||||
|
||||
|
||||
/* Block IDLE Status Register */
|
||||
#define REG_IDLE_STATUS 0x1410
|
||||
#define IDLE_STATUS_RXMAC 1 /* 1: RXMAC state machine is in non-IDLE state. 0: RXMAC is idling */
|
||||
#define IDLE_STATUS_TXMAC 2 /* 1: TXMAC state machine is in non-IDLE state. 0: TXMAC is idling */
|
||||
#define IDLE_STATUS_RXQ 4 /* 1: RXQ state machine is in non-IDLE state. 0: RXQ is idling */
|
||||
#define IDLE_STATUS_TXQ 8 /* 1: TXQ state machine is in non-IDLE state. 0: TXQ is idling */
|
||||
#define IDLE_STATUS_DMAR 0x10 /* 1: DMAR state machine is in non-IDLE state. 0: DMAR is idling */
|
||||
#define IDLE_STATUS_DMAW 0x20 /* 1: DMAW state machine is in non-IDLE state. 0: DMAW is idling */
|
||||
#define IDLE_STATUS_SMB 0x40 /* 1: SMB state machine is in non-IDLE state. 0: SMB is idling */
|
||||
#define IDLE_STATUS_CMB 0x80 /* 1: CMB state machine is in non-IDLE state. 0: CMB is idling */
|
||||
|
||||
/* MDIO Control Register */
|
||||
#define REG_MDIO_CTRL 0x1414
|
||||
#define MDIO_DATA_MASK 0xffff /* On MDIO write, the 16-bit control data to write to PHY MII management register */
|
||||
#define MDIO_DATA_SHIFT 0 /* On MDIO read, the 16-bit status data that was read from the PHY MII management register*/
|
||||
#define MDIO_REG_ADDR_MASK 0x1f /* MDIO register address */
|
||||
#define MDIO_REG_ADDR_SHIFT 16
|
||||
#define MDIO_RW 0x200000 /* 1: read, 0: write */
|
||||
#define MDIO_SUP_PREAMBLE 0x400000 /* Suppress preamble */
|
||||
#define MDIO_START 0x800000 /* Write 1 to initiate the MDIO master. And this bit is self cleared after one cycle*/
|
||||
#define MDIO_CLK_SEL_SHIFT 24
|
||||
#define MDIO_CLK_25_4 0
|
||||
#define MDIO_CLK_25_6 2
|
||||
#define MDIO_CLK_25_8 3
|
||||
#define MDIO_CLK_25_10 4
|
||||
#define MDIO_CLK_25_14 5
|
||||
#define MDIO_CLK_25_20 6
|
||||
#define MDIO_CLK_25_28 7
|
||||
#define MDIO_BUSY 0x8000000
|
||||
#define MDIO_AP_EN 0x10000000
|
||||
#define MDIO_WAIT_TIMES 10
|
||||
|
||||
/* MII PHY Status Register */
|
||||
#define REG_PHY_STATUS 0x1418
|
||||
#define PHY_STATUS_100M 0x20000
|
||||
#define PHY_STATUS_EMI_CA 0x40000
|
||||
|
||||
/* BIST Control and Status Register0 (for the Packet Memory) */
|
||||
#define REG_BIST0_CTRL 0x141c
|
||||
#define BIST0_NOW 0x1 /* 1: To trigger BIST0 logic. This bit stays high during the */
|
||||
/* BIST process and reset to zero when BIST is done */
|
||||
#define BIST0_SRAM_FAIL 0x2 /* 1: The SRAM failure is un-repairable because it has address */
|
||||
/* decoder failure or more than 1 cell stuck-to-x failure */
|
||||
#define BIST0_FUSE_FLAG 0x4 /* 1: Indicating one cell has been fixed */
|
||||
|
||||
/* BIST Control and Status Register1(for the retry buffer of PCI Express) */
|
||||
#define REG_BIST1_CTRL 0x1420
|
||||
#define BIST1_NOW 0x1 /* 1: To trigger BIST0 logic. This bit stays high during the */
|
||||
/* BIST process and reset to zero when BIST is done */
|
||||
#define BIST1_SRAM_FAIL 0x2 /* 1: The SRAM failure is un-repairable because it has address */
|
||||
/* decoder failure or more than 1 cell stuck-to-x failure.*/
|
||||
#define BIST1_FUSE_FLAG 0x4
|
||||
|
||||
/* SerDes Lock Detect Control and Status Register */
|
||||
#define REG_SERDES_LOCK 0x1424
|
||||
#define SERDES_LOCK_DETECT 1 /* 1: SerDes lock detected . This signal comes from Analog SerDes */
|
||||
#define SERDES_LOCK_DETECT_EN 2 /* 1: Enable SerDes Lock detect function */
|
||||
|
||||
/* MAC Control Register */
|
||||
#define REG_MAC_CTRL 0x1480
|
||||
#define MAC_CTRL_TX_EN 1 /* 1: Transmit Enable */
|
||||
#define MAC_CTRL_RX_EN 2 /* 1: Receive Enable */
|
||||
#define MAC_CTRL_TX_FLOW 4 /* 1: Transmit Flow Control Enable */
|
||||
#define MAC_CTRL_RX_FLOW 8 /* 1: Receive Flow Control Enable */
|
||||
#define MAC_CTRL_LOOPBACK 0x10 /* 1: Loop back at G/MII Interface */
|
||||
#define MAC_CTRL_DUPLX 0x20 /* 1: Full-duplex mode 0: Half-duplex mode */
|
||||
#define MAC_CTRL_ADD_CRC 0x40 /* 1: Instruct MAC to attach CRC on all egress Ethernet frames */
|
||||
#define MAC_CTRL_PAD 0x80 /* 1: Instruct MAC to pad short frames to 60-bytes, and then attach CRC. This bit has higher priority over CRC_EN */
|
||||
#define MAC_CTRL_LENCHK 0x100 /* 1: Instruct MAC to check if length field matches the real packet length */
|
||||
#define MAC_CTRL_HUGE_EN 0x200 /* 1: receive Jumbo frame enable */
|
||||
#define MAC_CTRL_PRMLEN_SHIFT 10 /* Preamble length */
|
||||
#define MAC_CTRL_PRMLEN_MASK 0xf
|
||||
#define MAC_CTRL_RMV_VLAN 0x4000 /* 1: to remove VLAN Tag automatically from all receive packets */
|
||||
#define MAC_CTRL_PROMIS_EN 0x8000 /* 1: Promiscuous Mode Enable */
|
||||
#define MAC_CTRL_TX_PAUSE 0x10000 /* 1: transmit test pause */
|
||||
#define MAC_CTRL_SCNT 0x20000 /* 1: shortcut slot time counter */
|
||||
#define MAC_CTRL_SRST_TX 0x40000 /* 1: synchronized reset Transmit MAC module */
|
||||
#define MAC_CTRL_TX_SIMURST 0x80000 /* 1: transmit simulation reset */
|
||||
#define MAC_CTRL_SPEED_SHIFT 20 /* 10: gigabit 01:10M/100M */
|
||||
#define MAC_CTRL_SPEED_MASK 0x300000
|
||||
#define MAC_CTRL_SPEED_1000 2
|
||||
#define MAC_CTRL_SPEED_10_100 1
|
||||
#define MAC_CTRL_DBG_TX_BKPRESURE 0x400000 /* 1: transmit maximum backoff (half-duplex test bit) */
|
||||
#define MAC_CTRL_TX_HUGE 0x800000 /* 1: transmit huge enable */
|
||||
#define MAC_CTRL_RX_CHKSUM_EN 0x1000000 /* 1: RX checksum enable */
|
||||
#define MAC_CTRL_MC_ALL_EN 0x2000000 /* 1: upload all multicast frame without error to system */
|
||||
#define MAC_CTRL_BC_EN 0x4000000 /* 1: upload all broadcast frame without error to system */
|
||||
#define MAC_CTRL_DBG 0x8000000 /* 1: upload all received frame to system (Debug Mode) */
|
||||
|
||||
/* MAC IPG/IFG Control Register */
|
||||
#define REG_MAC_IPG_IFG 0x1484
|
||||
#define MAC_IPG_IFG_IPGT_SHIFT 0 /* Desired back to back inter-packet gap. The default is 96-bit time */
|
||||
#define MAC_IPG_IFG_IPGT_MASK 0x7f
|
||||
#define MAC_IPG_IFG_MIFG_SHIFT 8 /* Minimum number of IFG to enforce in between RX frames */
|
||||
#define MAC_IPG_IFG_MIFG_MASK 0xff /* Frame gap below such IFP is dropped */
|
||||
#define MAC_IPG_IFG_IPGR1_SHIFT 16 /* 64bit Carrier-Sense window */
|
||||
#define MAC_IPG_IFG_IPGR1_MASK 0x7f
|
||||
#define MAC_IPG_IFG_IPGR2_SHIFT 24 /* 96-bit IPG window */
|
||||
#define MAC_IPG_IFG_IPGR2_MASK 0x7f
|
||||
|
||||
/* MAC STATION ADDRESS */
|
||||
#define REG_MAC_STA_ADDR 0x1488
|
||||
|
||||
/* Hash table for multicast address */
|
||||
#define REG_RX_HASH_TABLE 0x1490
|
||||
|
||||
|
||||
/* MAC Half-Duplex Control Register */
|
||||
#define REG_MAC_HALF_DUPLX_CTRL 0x1498
|
||||
#define MAC_HALF_DUPLX_CTRL_LCOL_SHIFT 0 /* Collision Window */
|
||||
#define MAC_HALF_DUPLX_CTRL_LCOL_MASK 0x3ff
|
||||
#define MAC_HALF_DUPLX_CTRL_RETRY_SHIFT 12 /* Retransmission maximum, afterwards the packet will be discarded */
|
||||
#define MAC_HALF_DUPLX_CTRL_RETRY_MASK 0xf
|
||||
#define MAC_HALF_DUPLX_CTRL_EXC_DEF_EN 0x10000 /* 1: Allow the transmission of a packet which has been excessively deferred */
|
||||
#define MAC_HALF_DUPLX_CTRL_NO_BACK_C 0x20000 /* 1: No back-off on collision, immediately start the retransmission */
|
||||
#define MAC_HALF_DUPLX_CTRL_NO_BACK_P 0x40000 /* 1: No back-off on backpressure, immediately start the transmission after back pressure */
|
||||
#define MAC_HALF_DUPLX_CTRL_ABEBE 0x80000 /* 1: Alternative Binary Exponential Back-off Enabled */
|
||||
#define MAC_HALF_DUPLX_CTRL_ABEBT_SHIFT 20 /* Maximum binary exponential number */
|
||||
#define MAC_HALF_DUPLX_CTRL_ABEBT_MASK 0xf
|
||||
#define MAC_HALF_DUPLX_CTRL_JAMIPG_SHIFT 24 /* IPG to start JAM for collision based flow control in half-duplex */
|
||||
#define MAC_HALF_DUPLX_CTRL_JAMIPG_MASK 0xf /* mode. In unit of 8-bit time */
|
||||
|
||||
/* Maximum Frame Length Control Register */
|
||||
#define REG_MTU 0x149c
|
||||
|
||||
/* Wake-On-Lan control register */
|
||||
#define REG_WOL_CTRL 0x14a0
|
||||
#define WOL_PATTERN_EN 0x00000001
|
||||
#define WOL_PATTERN_PME_EN 0x00000002
|
||||
#define WOL_MAGIC_EN 0x00000004
|
||||
#define WOL_MAGIC_PME_EN 0x00000008
|
||||
#define WOL_LINK_CHG_EN 0x00000010
|
||||
#define WOL_LINK_CHG_PME_EN 0x00000020
|
||||
#define WOL_PATTERN_ST 0x00000100
|
||||
#define WOL_MAGIC_ST 0x00000200
|
||||
#define WOL_LINKCHG_ST 0x00000400
|
||||
#define WOL_CLK_SWITCH_EN 0x00008000
|
||||
#define WOL_PT0_EN 0x00010000
|
||||
#define WOL_PT1_EN 0x00020000
|
||||
#define WOL_PT2_EN 0x00040000
|
||||
#define WOL_PT3_EN 0x00080000
|
||||
#define WOL_PT4_EN 0x00100000
|
||||
#define WOL_PT5_EN 0x00200000
|
||||
#define WOL_PT6_EN 0x00400000
|
||||
/* WOL Length ( 2 DWORD ) */
|
||||
#define REG_WOL_PATTERN_LEN 0x14a4
|
||||
#define WOL_PT_LEN_MASK 0x7f
|
||||
#define WOL_PT0_LEN_SHIFT 0
|
||||
#define WOL_PT1_LEN_SHIFT 8
|
||||
#define WOL_PT2_LEN_SHIFT 16
|
||||
#define WOL_PT3_LEN_SHIFT 24
|
||||
#define WOL_PT4_LEN_SHIFT 0
|
||||
#define WOL_PT5_LEN_SHIFT 8
|
||||
#define WOL_PT6_LEN_SHIFT 16
|
||||
|
||||
/* Internal SRAM Partition Register */
|
||||
#define REG_SRAM_TRD_ADDR 0x1518
|
||||
#define REG_SRAM_TRD_LEN 0x151C
|
||||
#define REG_SRAM_RXF_ADDR 0x1520
|
||||
#define REG_SRAM_RXF_LEN 0x1524
|
||||
#define REG_SRAM_TXF_ADDR 0x1528
|
||||
#define REG_SRAM_TXF_LEN 0x152C
|
||||
#define REG_SRAM_TCPH_ADDR 0x1530
|
||||
#define REG_SRAM_PKTH_ADDR 0x1532
|
||||
|
||||
/* Load Ptr Register */
|
||||
#define REG_LOAD_PTR 0x1534 /* Software sets this bit after the initialization of the head and tail */
|
||||
|
||||
/*
|
||||
* addresses of all descriptors, as well as the following descriptor
|
||||
* control register, which triggers each function block to load the head
|
||||
* pointer to prepare for the operation. This bit is then self-cleared
|
||||
* after one cycle.
|
||||
*/
|
||||
|
||||
/* Descriptor Control register */
|
||||
#define REG_RXF3_BASE_ADDR_HI 0x153C
|
||||
#define REG_DESC_BASE_ADDR_HI 0x1540
|
||||
#define REG_RXF0_BASE_ADDR_HI 0x1540 /* share with DESC BASE ADDR HI */
|
||||
#define REG_HOST_RXF0_PAGE0_LO 0x1544
|
||||
#define REG_HOST_RXF0_PAGE1_LO 0x1548
|
||||
#define REG_TPD_BASE_ADDR_LO 0x154C
|
||||
#define REG_RXF1_BASE_ADDR_HI 0x1550
|
||||
#define REG_RXF2_BASE_ADDR_HI 0x1554
|
||||
#define REG_HOST_RXFPAGE_SIZE 0x1558
|
||||
#define REG_TPD_RING_SIZE 0x155C
|
||||
/* RSS about */
|
||||
#define REG_RSS_KEY0 0x14B0
|
||||
#define REG_RSS_KEY1 0x14B4
|
||||
#define REG_RSS_KEY2 0x14B8
|
||||
#define REG_RSS_KEY3 0x14BC
|
||||
#define REG_RSS_KEY4 0x14C0
|
||||
#define REG_RSS_KEY5 0x14C4
|
||||
#define REG_RSS_KEY6 0x14C8
|
||||
#define REG_RSS_KEY7 0x14CC
|
||||
#define REG_RSS_KEY8 0x14D0
|
||||
#define REG_RSS_KEY9 0x14D4
|
||||
#define REG_IDT_TABLE4 0x14E0
|
||||
#define REG_IDT_TABLE5 0x14E4
|
||||
#define REG_IDT_TABLE6 0x14E8
|
||||
#define REG_IDT_TABLE7 0x14EC
|
||||
#define REG_IDT_TABLE0 0x1560
|
||||
#define REG_IDT_TABLE1 0x1564
|
||||
#define REG_IDT_TABLE2 0x1568
|
||||
#define REG_IDT_TABLE3 0x156C
|
||||
#define REG_IDT_TABLE REG_IDT_TABLE0
|
||||
#define REG_RSS_HASH_VALUE 0x1570
|
||||
#define REG_RSS_HASH_FLAG 0x1574
|
||||
#define REG_BASE_CPU_NUMBER 0x157C
|
||||
|
||||
|
||||
/* TXQ Control Register */
|
||||
#define REG_TXQ_CTRL 0x1580
|
||||
#define TXQ_CTRL_NUM_TPD_BURST_MASK 0xF
|
||||
#define TXQ_CTRL_NUM_TPD_BURST_SHIFT 0
|
||||
#define TXQ_CTRL_EN 0x20 /* 1: Enable TXQ */
|
||||
#define TXQ_CTRL_ENH_MODE 0x40 /* Performance enhancement mode, in which up to two back-to-back DMA read commands might be dispatched. */
|
||||
#define TXQ_CTRL_TXF_BURST_NUM_SHIFT 16 /* Number of data byte to read in a cache-aligned burst. Each SRAM entry is 8-byte in length. */
|
||||
#define TXQ_CTRL_TXF_BURST_NUM_MASK 0xffff
|
||||
|
||||
/* Jumbo packet Threshold for task offload */
|
||||
#define REG_TX_EARLY_TH 0x1584 /* Jumbo frame threshold in QWORD unit. Packet greater than */
|
||||
/* JUMBO_TASK_OFFLOAD_THRESHOLD will not be task offloaded. */
|
||||
#define TX_TX_EARLY_TH_MASK 0x7ff
|
||||
#define TX_TX_EARLY_TH_SHIFT 0
|
||||
|
||||
|
||||
/* RXQ Control Register */
|
||||
#define REG_RXQ_CTRL 0x15A0
|
||||
#define RXQ_CTRL_PBA_ALIGN_32 0 /* rx-packet alignment */
|
||||
#define RXQ_CTRL_PBA_ALIGN_64 1
|
||||
#define RXQ_CTRL_PBA_ALIGN_128 2
|
||||
#define RXQ_CTRL_PBA_ALIGN_256 3
|
||||
#define RXQ_CTRL_Q1_EN 0x10
|
||||
#define RXQ_CTRL_Q2_EN 0x20
|
||||
#define RXQ_CTRL_Q3_EN 0x40
|
||||
#define RXQ_CTRL_IPV6_XSUM_VERIFY_EN 0x80
|
||||
#define RXQ_CTRL_HASH_TLEN_SHIFT 8
|
||||
#define RXQ_CTRL_HASH_TLEN_MASK 0xFF
|
||||
#define RXQ_CTRL_HASH_TYPE_IPV4 0x10000
|
||||
#define RXQ_CTRL_HASH_TYPE_IPV4_TCP 0x20000
|
||||
#define RXQ_CTRL_HASH_TYPE_IPV6 0x40000
|
||||
#define RXQ_CTRL_HASH_TYPE_IPV6_TCP 0x80000
|
||||
#define RXQ_CTRL_RSS_MODE_DISABLE 0
|
||||
#define RXQ_CTRL_RSS_MODE_SQSINT 0x4000000
|
||||
#define RXQ_CTRL_RSS_MODE_MQUESINT 0x8000000
|
||||
#define RXQ_CTRL_RSS_MODE_MQUEMINT 0xC000000
|
||||
#define RXQ_CTRL_NIP_QUEUE_SEL_TBL 0x10000000
|
||||
#define RXQ_CTRL_HASH_ENABLE 0x20000000
|
||||
#define RXQ_CTRL_CUT_THRU_EN 0x40000000
|
||||
#define RXQ_CTRL_EN 0x80000000
|
||||
|
||||
/* Rx jumbo packet threshold and rrd retirement timer */
|
||||
#define REG_RXQ_JMBOSZ_RRDTIM 0x15A4
|
||||
/*
|
||||
* Jumbo packet threshold for non-VLAN packet, in QWORD (64-bit) unit.
|
||||
* When the packet length greater than or equal to this value, RXQ
|
||||
* shall start cut-through forwarding of the received packet.
|
||||
*/
|
||||
#define RXQ_JMBOSZ_TH_MASK 0x7ff
|
||||
#define RXQ_JMBOSZ_TH_SHIFT 0 /* RRD retirement timer. Decrement by 1 after every 512ns passes*/
|
||||
#define RXQ_JMBO_LKAH_MASK 0xf
|
||||
#define RXQ_JMBO_LKAH_SHIFT 11
|
||||
|
||||
/* RXF flow control register */
|
||||
#define REG_RXQ_RXF_PAUSE_THRESH 0x15A8
|
||||
#define RXQ_RXF_PAUSE_TH_HI_SHIFT 0
|
||||
#define RXQ_RXF_PAUSE_TH_HI_MASK 0xfff
|
||||
#define RXQ_RXF_PAUSE_TH_LO_SHIFT 16
|
||||
#define RXQ_RXF_PAUSE_TH_LO_MASK 0xfff
|
||||
|
||||
|
||||
/* DMA Engine Control Register */
|
||||
#define REG_DMA_CTRL 0x15C0
|
||||
#define DMA_CTRL_DMAR_IN_ORDER 0x1
|
||||
#define DMA_CTRL_DMAR_ENH_ORDER 0x2
|
||||
#define DMA_CTRL_DMAR_OUT_ORDER 0x4
|
||||
#define DMA_CTRL_RCB_VALUE 0x8
|
||||
#define DMA_CTRL_DMAR_BURST_LEN_SHIFT 4
|
||||
#define DMA_CTRL_DMAR_BURST_LEN_MASK 7
|
||||
#define DMA_CTRL_DMAW_BURST_LEN_SHIFT 7
|
||||
#define DMA_CTRL_DMAW_BURST_LEN_MASK 7
|
||||
#define DMA_CTRL_DMAR_REQ_PRI 0x400
|
||||
#define DMA_CTRL_DMAR_DLY_CNT_MASK 0x1F
|
||||
#define DMA_CTRL_DMAR_DLY_CNT_SHIFT 11
|
||||
#define DMA_CTRL_DMAW_DLY_CNT_MASK 0xF
|
||||
#define DMA_CTRL_DMAW_DLY_CNT_SHIFT 16
|
||||
#define DMA_CTRL_TXCMB_EN 0x100000
|
||||
#define DMA_CTRL_RXCMB_EN 0x200000
|
||||
|
||||
|
||||
/* CMB/SMB Control Register */
|
||||
#define REG_SMB_STAT_TIMER 0x15C4
|
||||
#define REG_TRIG_RRD_THRESH 0x15CA
|
||||
#define REG_TRIG_TPD_THRESH 0x15C8
|
||||
#define REG_TRIG_TXTIMER 0x15CC
|
||||
#define REG_TRIG_RXTIMER 0x15CE
|
||||
|
||||
/* HOST RXF Page 1,2,3 address */
|
||||
#define REG_HOST_RXF1_PAGE0_LO 0x15D0
|
||||
#define REG_HOST_RXF1_PAGE1_LO 0x15D4
|
||||
#define REG_HOST_RXF2_PAGE0_LO 0x15D8
|
||||
#define REG_HOST_RXF2_PAGE1_LO 0x15DC
|
||||
#define REG_HOST_RXF3_PAGE0_LO 0x15E0
|
||||
#define REG_HOST_RXF3_PAGE1_LO 0x15E4
|
||||
|
||||
/* Mail box */
|
||||
#define REG_MB_RXF1_RADDR 0x15B4
|
||||
#define REG_MB_RXF2_RADDR 0x15B8
|
||||
#define REG_MB_RXF3_RADDR 0x15BC
|
||||
#define REG_MB_TPD_PROD_IDX 0x15F0
|
||||
|
||||
/* RXF-Page 0-3 PageNo & Valid bit */
|
||||
#define REG_HOST_RXF0_PAGE0_VLD 0x15F4
|
||||
#define HOST_RXF_VALID 1
|
||||
#define HOST_RXF_PAGENO_SHIFT 1
|
||||
#define HOST_RXF_PAGENO_MASK 0x7F
|
||||
#define REG_HOST_RXF0_PAGE1_VLD 0x15F5
|
||||
#define REG_HOST_RXF1_PAGE0_VLD 0x15F6
|
||||
#define REG_HOST_RXF1_PAGE1_VLD 0x15F7
|
||||
#define REG_HOST_RXF2_PAGE0_VLD 0x15F8
|
||||
#define REG_HOST_RXF2_PAGE1_VLD 0x15F9
|
||||
#define REG_HOST_RXF3_PAGE0_VLD 0x15FA
|
||||
#define REG_HOST_RXF3_PAGE1_VLD 0x15FB
|
||||
|
||||
/* Interrupt Status Register */
|
||||
#define REG_ISR 0x1600
|
||||
#define ISR_SMB 1
|
||||
#define ISR_TIMER 2 /* Interrupt when Timer is counted down to zero */
|
||||
/*
|
||||
* Software manual interrupt, for debug. Set when SW_MAN_INT_EN is set
|
||||
* in Table 51 Selene Master Control Register (Offset 0x1400).
|
||||
*/
|
||||
#define ISR_MANUAL 4
|
||||
#define ISR_HW_RXF_OV 8 /* RXF overflow interrupt */
|
||||
#define ISR_HOST_RXF0_OV 0x10
|
||||
#define ISR_HOST_RXF1_OV 0x20
|
||||
#define ISR_HOST_RXF2_OV 0x40
|
||||
#define ISR_HOST_RXF3_OV 0x80
|
||||
#define ISR_TXF_UN 0x100
|
||||
#define ISR_RX0_PAGE_FULL 0x200
|
||||
#define ISR_DMAR_TO_RST 0x400
|
||||
#define ISR_DMAW_TO_RST 0x800
|
||||
#define ISR_GPHY 0x1000
|
||||
#define ISR_TX_CREDIT 0x2000
|
||||
#define ISR_GPHY_LPW 0x4000 /* GPHY low power state interrupt */
|
||||
#define ISR_RX_PKT 0x10000 /* One packet received, triggered by RFD */
|
||||
#define ISR_TX_PKT 0x20000 /* One packet transmitted, triggered by TPD */
|
||||
#define ISR_TX_DMA 0x40000
|
||||
#define ISR_RX_PKT_1 0x80000
|
||||
#define ISR_RX_PKT_2 0x100000
|
||||
#define ISR_RX_PKT_3 0x200000
|
||||
#define ISR_MAC_RX 0x400000
|
||||
#define ISR_MAC_TX 0x800000
|
||||
#define ISR_UR_DETECTED 0x1000000
|
||||
#define ISR_FERR_DETECTED 0x2000000
|
||||
#define ISR_NFERR_DETECTED 0x4000000
|
||||
#define ISR_CERR_DETECTED 0x8000000
|
||||
#define ISR_PHY_LINKDOWN 0x10000000
|
||||
#define ISR_DIS_INT 0x80000000
|
||||
|
||||
|
||||
/* Interrupt Mask Register */
|
||||
#define REG_IMR 0x1604
|
||||
|
||||
|
||||
#define IMR_NORMAL_MASK (\
|
||||
ISR_SMB |\
|
||||
ISR_TXF_UN |\
|
||||
ISR_HW_RXF_OV |\
|
||||
ISR_HOST_RXF0_OV|\
|
||||
ISR_MANUAL |\
|
||||
ISR_GPHY |\
|
||||
ISR_GPHY_LPW |\
|
||||
ISR_DMAR_TO_RST |\
|
||||
ISR_DMAW_TO_RST |\
|
||||
ISR_PHY_LINKDOWN|\
|
||||
ISR_RX_PKT |\
|
||||
ISR_TX_PKT)
|
||||
|
||||
#define ISR_TX_EVENT (ISR_TXF_UN | ISR_TX_PKT)
|
||||
#define ISR_RX_EVENT (ISR_HOST_RXF0_OV | ISR_HW_RXF_OV | ISR_RX_PKT)
|
||||
|
||||
#define REG_MAC_RX_STATUS_BIN 0x1700
|
||||
#define REG_MAC_RX_STATUS_END 0x175c
|
||||
#define REG_MAC_TX_STATUS_BIN 0x1760
|
||||
#define REG_MAC_TX_STATUS_END 0x17c0
|
||||
|
||||
/* Hardware Offset Register */
|
||||
#define REG_HOST_RXF0_PAGEOFF 0x1800
|
||||
#define REG_TPD_CONS_IDX 0x1804
|
||||
#define REG_HOST_RXF1_PAGEOFF 0x1808
|
||||
#define REG_HOST_RXF2_PAGEOFF 0x180C
|
||||
#define REG_HOST_RXF3_PAGEOFF 0x1810
|
||||
|
||||
/* RXF-Page 0-3 Offset DMA Address */
|
||||
#define REG_HOST_RXF0_MB0_LO 0x1820
|
||||
#define REG_HOST_RXF0_MB1_LO 0x1824
|
||||
#define REG_HOST_RXF1_MB0_LO 0x1828
|
||||
#define REG_HOST_RXF1_MB1_LO 0x182C
|
||||
#define REG_HOST_RXF2_MB0_LO 0x1830
|
||||
#define REG_HOST_RXF2_MB1_LO 0x1834
|
||||
#define REG_HOST_RXF3_MB0_LO 0x1838
|
||||
#define REG_HOST_RXF3_MB1_LO 0x183C
|
||||
|
||||
/* Tpd CMB DMA Address */
|
||||
#define REG_HOST_TX_CMB_LO 0x1840
|
||||
#define REG_HOST_SMB_ADDR_LO 0x1844
|
||||
|
||||
/* DEBUG ADDR */
|
||||
#define REG_DEBUG_DATA0 0x1900
|
||||
#define REG_DEBUG_DATA1 0x1904
|
||||
|
||||
/***************************** MII definition ***************************************/
|
||||
/* PHY Common Register */
|
||||
#define MII_AT001_PSCR 0x10
|
||||
#define MII_AT001_PSSR 0x11
|
||||
#define MII_INT_CTRL 0x12
|
||||
#define MII_INT_STATUS 0x13
|
||||
#define MII_SMARTSPEED 0x14
|
||||
#define MII_LBRERROR 0x18
|
||||
#define MII_RESV2 0x1a
|
||||
|
||||
#define MII_DBG_ADDR 0x1D
|
||||
#define MII_DBG_DATA 0x1E
|
||||
|
||||
/* Autoneg Advertisement Register */
|
||||
#define MII_AR_DEFAULT_CAP_MASK 0
|
||||
|
||||
/* 1000BASE-T Control Register */
|
||||
#define MII_AT001_CR_1000T_SPEED_MASK \
|
||||
(ADVERTISE_1000FULL | ADVERTISE_1000HALF)
|
||||
#define MII_AT001_CR_1000T_DEFAULT_CAP_MASK MII_AT001_CR_1000T_SPEED_MASK
|
||||
|
||||
/* AT001 PHY Specific Control Register */
|
||||
#define MII_AT001_PSCR_JABBER_DISABLE 0x0001 /* 1=Jabber Function disabled */
|
||||
#define MII_AT001_PSCR_POLARITY_REVERSAL 0x0002 /* 1=Polarity Reversal enabled */
|
||||
#define MII_AT001_PSCR_SQE_TEST 0x0004 /* 1=SQE Test enabled */
|
||||
#define MII_AT001_PSCR_MAC_POWERDOWN 0x0008
|
||||
#define MII_AT001_PSCR_CLK125_DISABLE 0x0010 /* 1=CLK125 low,
|
||||
* 0=CLK125 toggling
|
||||
*/
|
||||
#define MII_AT001_PSCR_MDI_MANUAL_MODE 0x0000 /* MDI Crossover Mode bits 6:5 */
|
||||
/* Manual MDI configuration */
|
||||
#define MII_AT001_PSCR_MDIX_MANUAL_MODE 0x0020 /* Manual MDIX configuration */
|
||||
#define MII_AT001_PSCR_AUTO_X_1000T 0x0040 /* 1000BASE-T: Auto crossover,
|
||||
* 100BASE-TX/10BASE-T:
|
||||
* MDI Mode
|
||||
*/
|
||||
#define MII_AT001_PSCR_AUTO_X_MODE 0x0060 /* Auto crossover enabled
|
||||
* all speeds.
|
||||
*/
|
||||
#define MII_AT001_PSCR_10BT_EXT_DIST_ENABLE 0x0080
|
||||
/* 1=Enable Extended 10BASE-T distance
|
||||
* (Lower 10BASE-T RX Threshold)
|
||||
* 0=Normal 10BASE-T RX Threshold */
|
||||
#define MII_AT001_PSCR_MII_5BIT_ENABLE 0x0100
|
||||
/* 1=5-Bit interface in 100BASE-TX
|
||||
* 0=MII interface in 100BASE-TX */
|
||||
#define MII_AT001_PSCR_SCRAMBLER_DISABLE 0x0200 /* 1=Scrambler disable */
|
||||
#define MII_AT001_PSCR_FORCE_LINK_GOOD 0x0400 /* 1=Force link good */
|
||||
#define MII_AT001_PSCR_ASSERT_CRS_ON_TX 0x0800 /* 1=Assert CRS on Transmit */
|
||||
#define MII_AT001_PSCR_POLARITY_REVERSAL_SHIFT 1
|
||||
#define MII_AT001_PSCR_AUTO_X_MODE_SHIFT 5
|
||||
#define MII_AT001_PSCR_10BT_EXT_DIST_ENABLE_SHIFT 7
|
||||
/* AT001 PHY Specific Status Register */
|
||||
#define MII_AT001_PSSR_SPD_DPLX_RESOLVED 0x0800 /* 1=Speed & Duplex resolved */
|
||||
#define MII_AT001_PSSR_DPLX 0x2000 /* 1=Duplex 0=Half Duplex */
|
||||
#define MII_AT001_PSSR_SPEED 0xC000 /* Speed, bits 14:15 */
|
||||
#define MII_AT001_PSSR_10MBS 0x0000 /* 00=10Mbs */
|
||||
#define MII_AT001_PSSR_100MBS 0x4000 /* 01=100Mbs */
|
||||
#define MII_AT001_PSSR_1000MBS 0x8000 /* 10=1000Mbs */
|
||||
|
||||
#endif /*_ATHL1E_HW_H_*/
|
2559
addons/atl1e/src/3.10.108/atl1e_main.c
Normal file
2559
addons/atl1e/src/3.10.108/atl1e_main.c
Normal file
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