linux/drivers/s390/scsi/zfcp_cfdc.c
<<
>>
Prefs
   1/*
   2 * zfcp device driver
   3 *
   4 * Userspace interface for accessing the
   5 * Access Control Lists / Control File Data Channel
   6 *
   7 * Copyright IBM Corporation 2008, 2009
   8 */
   9
  10#define KMSG_COMPONENT "zfcp"
  11#define pr_fmt(fmt) KMSG_COMPONENT ": " fmt
  12
  13#include <linux/types.h>
  14#include <linux/miscdevice.h>
  15#include <asm/ccwdev.h>
  16#include "zfcp_def.h"
  17#include "zfcp_ext.h"
  18#include "zfcp_fsf.h"
  19
  20#define ZFCP_CFDC_CMND_DOWNLOAD_NORMAL          0x00010001
  21#define ZFCP_CFDC_CMND_DOWNLOAD_FORCE           0x00010101
  22#define ZFCP_CFDC_CMND_FULL_ACCESS              0x00000201
  23#define ZFCP_CFDC_CMND_RESTRICTED_ACCESS        0x00000401
  24#define ZFCP_CFDC_CMND_UPLOAD                   0x00010002
  25
  26#define ZFCP_CFDC_DOWNLOAD                      0x00000001
  27#define ZFCP_CFDC_UPLOAD                        0x00000002
  28#define ZFCP_CFDC_WITH_CONTROL_FILE             0x00010000
  29
  30#define ZFCP_CFDC_IOC_MAGIC                     0xDD
  31#define ZFCP_CFDC_IOC \
  32        _IOWR(ZFCP_CFDC_IOC_MAGIC, 0, struct zfcp_cfdc_data)
  33
  34/**
  35 * struct zfcp_cfdc_data - data for ioctl cfdc interface
  36 * @signature: request signature
  37 * @devno: FCP adapter device number
  38 * @command: command code
  39 * @fsf_status: returns status of FSF command to userspace
  40 * @fsf_status_qual: returned to userspace
  41 * @payloads: access conflicts list
  42 * @control_file: access control table
  43 */
  44struct zfcp_cfdc_data {
  45        u32 signature;
  46        u32 devno;
  47        u32 command;
  48        u32 fsf_status;
  49        u8  fsf_status_qual[FSF_STATUS_QUALIFIER_SIZE];
  50        u8  payloads[256];
  51        u8  control_file[0];
  52};
  53
  54static int zfcp_cfdc_copy_from_user(struct scatterlist *sg,
  55                                    void __user *user_buffer)
  56{
  57        unsigned int length;
  58        unsigned int size = ZFCP_CFDC_MAX_SIZE;
  59
  60        while (size) {
  61                length = min((unsigned int)size, sg->length);
  62                if (copy_from_user(sg_virt(sg++), user_buffer, length))
  63                        return -EFAULT;
  64                user_buffer += length;
  65                size -= length;
  66        }
  67        return 0;
  68}
  69
  70static int zfcp_cfdc_copy_to_user(void __user  *user_buffer,
  71                                  struct scatterlist *sg)
  72{
  73        unsigned int length;
  74        unsigned int size = ZFCP_CFDC_MAX_SIZE;
  75
  76        while (size) {
  77                length = min((unsigned int) size, sg->length);
  78                if (copy_to_user(user_buffer, sg_virt(sg++), length))
  79                        return -EFAULT;
  80                user_buffer += length;
  81                size -= length;
  82        }
  83        return 0;
  84}
  85
  86static struct zfcp_adapter *zfcp_cfdc_get_adapter(u32 devno)
  87{
  88        char busid[9];
  89        struct ccw_device *ccwdev;
  90        struct zfcp_adapter *adapter = NULL;
  91
  92        snprintf(busid, sizeof(busid), "0.0.%04x", devno);
  93        ccwdev = get_ccwdev_by_busid(&zfcp_ccw_driver, busid);
  94        if (!ccwdev)
  95                goto out;
  96
  97        adapter = dev_get_drvdata(&ccwdev->dev);
  98        if (!adapter)
  99                goto out_put;
 100
 101        zfcp_adapter_get(adapter);
 102out_put:
 103        put_device(&ccwdev->dev);
 104out:
 105        return adapter;
 106}
 107
 108static int zfcp_cfdc_set_fsf(struct zfcp_fsf_cfdc *fsf_cfdc, int command)
 109{
 110        switch (command) {
 111        case ZFCP_CFDC_CMND_DOWNLOAD_NORMAL:
 112                fsf_cfdc->command = FSF_QTCB_DOWNLOAD_CONTROL_FILE;
 113                fsf_cfdc->option = FSF_CFDC_OPTION_NORMAL_MODE;
 114                break;
 115        case ZFCP_CFDC_CMND_DOWNLOAD_FORCE:
 116                fsf_cfdc->command = FSF_QTCB_DOWNLOAD_CONTROL_FILE;
 117                fsf_cfdc->option = FSF_CFDC_OPTION_FORCE;
 118                break;
 119        case ZFCP_CFDC_CMND_FULL_ACCESS:
 120                fsf_cfdc->command = FSF_QTCB_DOWNLOAD_CONTROL_FILE;
 121                fsf_cfdc->option = FSF_CFDC_OPTION_FULL_ACCESS;
 122                break;
 123        case ZFCP_CFDC_CMND_RESTRICTED_ACCESS:
 124                fsf_cfdc->command = FSF_QTCB_DOWNLOAD_CONTROL_FILE;
 125                fsf_cfdc->option = FSF_CFDC_OPTION_RESTRICTED_ACCESS;
 126                break;
 127        case ZFCP_CFDC_CMND_UPLOAD:
 128                fsf_cfdc->command = FSF_QTCB_UPLOAD_CONTROL_FILE;
 129                fsf_cfdc->option = 0;
 130                break;
 131        default:
 132                return -EINVAL;
 133        }
 134
 135        return 0;
 136}
 137
 138static int zfcp_cfdc_sg_setup(int command, struct scatterlist *sg,
 139                              u8 __user *control_file)
 140{
 141        int retval;
 142        retval = zfcp_sg_setup_table(sg, ZFCP_CFDC_PAGES);
 143        if (retval)
 144                return retval;
 145
 146        sg[ZFCP_CFDC_PAGES - 1].length = ZFCP_CFDC_MAX_SIZE % PAGE_SIZE;
 147
 148        if (command & ZFCP_CFDC_WITH_CONTROL_FILE &&
 149            command & ZFCP_CFDC_DOWNLOAD) {
 150                retval = zfcp_cfdc_copy_from_user(sg, control_file);
 151                if (retval) {
 152                        zfcp_sg_free_table(sg, ZFCP_CFDC_PAGES);
 153                        return -EFAULT;
 154                }
 155        }
 156
 157        return 0;
 158}
 159
 160static void zfcp_cfdc_req_to_sense(struct zfcp_cfdc_data *data,
 161                                   struct zfcp_fsf_req *req)
 162{
 163        data->fsf_status = req->qtcb->header.fsf_status;
 164        memcpy(&data->fsf_status_qual, &req->qtcb->header.fsf_status_qual,
 165               sizeof(union fsf_status_qual));
 166        memcpy(&data->payloads, &req->qtcb->bottom.support.els,
 167               sizeof(req->qtcb->bottom.support.els));
 168}
 169
 170static long zfcp_cfdc_dev_ioctl(struct file *file, unsigned int command,
 171                                unsigned long buffer)
 172{
 173        struct zfcp_cfdc_data *data;
 174        struct zfcp_cfdc_data __user *data_user;
 175        struct zfcp_adapter *adapter;
 176        struct zfcp_fsf_req *req;
 177        struct zfcp_fsf_cfdc *fsf_cfdc;
 178        int retval;
 179
 180        if (command != ZFCP_CFDC_IOC)
 181                return -ENOTTY;
 182
 183        data_user = (void __user *) buffer;
 184        if (!data_user)
 185                return -EINVAL;
 186
 187        fsf_cfdc = kmalloc(sizeof(struct zfcp_fsf_cfdc), GFP_KERNEL);
 188        if (!fsf_cfdc)
 189                return -ENOMEM;
 190
 191        data = kmalloc(sizeof(struct zfcp_cfdc_data), GFP_KERNEL);
 192        if (!data) {
 193                retval = -ENOMEM;
 194                goto no_mem_sense;
 195        }
 196
 197        retval = copy_from_user(data, data_user, sizeof(*data));
 198        if (retval) {
 199                retval = -EFAULT;
 200                goto free_buffer;
 201        }
 202
 203        if (data->signature != 0xCFDCACDF) {
 204                retval = -EINVAL;
 205                goto free_buffer;
 206        }
 207
 208        retval = zfcp_cfdc_set_fsf(fsf_cfdc, data->command);
 209
 210        adapter = zfcp_cfdc_get_adapter(data->devno);
 211        if (!adapter) {
 212                retval = -ENXIO;
 213                goto free_buffer;
 214        }
 215        zfcp_adapter_get(adapter);
 216
 217        retval = zfcp_cfdc_sg_setup(data->command, fsf_cfdc->sg,
 218                                    data_user->control_file);
 219        if (retval)
 220                goto adapter_put;
 221        req = zfcp_fsf_control_file(adapter, fsf_cfdc);
 222        if (IS_ERR(req)) {
 223                retval = PTR_ERR(req);
 224                goto free_sg;
 225        }
 226
 227        if (req->status & ZFCP_STATUS_FSFREQ_ERROR) {
 228                retval = -ENXIO;
 229                goto free_fsf;
 230        }
 231
 232        zfcp_cfdc_req_to_sense(data, req);
 233        retval = copy_to_user(data_user, data, sizeof(*data_user));
 234        if (retval) {
 235                retval = -EFAULT;
 236                goto free_fsf;
 237        }
 238
 239        if (data->command & ZFCP_CFDC_UPLOAD)
 240                retval = zfcp_cfdc_copy_to_user(&data_user->control_file,
 241                                                fsf_cfdc->sg);
 242
 243 free_fsf:
 244        zfcp_fsf_req_free(req);
 245 free_sg:
 246        zfcp_sg_free_table(fsf_cfdc->sg, ZFCP_CFDC_PAGES);
 247 adapter_put:
 248        zfcp_adapter_put(adapter);
 249 free_buffer:
 250        kfree(data);
 251 no_mem_sense:
 252        kfree(fsf_cfdc);
 253        return retval;
 254}
 255
 256static const struct file_operations zfcp_cfdc_fops = {
 257        .unlocked_ioctl = zfcp_cfdc_dev_ioctl,
 258#ifdef CONFIG_COMPAT
 259        .compat_ioctl = zfcp_cfdc_dev_ioctl
 260#endif
 261};
 262
 263struct miscdevice zfcp_cfdc_misc = {
 264        .minor = MISC_DYNAMIC_MINOR,
 265        .name = "zfcp_cfdc",
 266        .fops = &zfcp_cfdc_fops,
 267};
 268