linux-old/drivers/s390/block/dasd_fba.c
<<
>>
Prefs
   1/* 
   2 * File...........: linux/drivers/s390/block/dasd_fba.c
   3 * Author(s)......: Holger Smolinski <Holger.Smolinski@de.ibm.com>
   4 * Bugreports.to..: <Linux390@de.ibm.com>
   5 * (C) IBM Corporation, IBM Deutschland Entwicklung GmbH, 1999,2000
   6 *
   7 * $Revision: 1.50 $
   8 *
   9 * History of changes
  10 *          fixed partition handling and HDIO_GETGEO
  11 */
  12
  13#include <linux/config.h>
  14#include <linux/stddef.h>
  15#include <linux/kernel.h>
  16#include <asm/debug.h>
  17
  18#include <linux/slab.h>
  19#include <linux/hdreg.h>        /* HDIO_GETGEO                      */
  20#include <linux/blk.h>
  21
  22#include <asm/ccwcache.h>
  23#include <asm/idals.h>
  24#include <asm/ebcdic.h>
  25#include <asm/io.h>
  26#include <asm/irq.h>
  27#include <asm/s390dyn.h>
  28
  29#include "dasd_int.h"
  30#include "dasd_fba.h"
  31#include "dasd_3370_erp.h"
  32#include "dasd_9336_erp.h"
  33
  34#ifdef PRINTK_HEADER
  35#undef PRINTK_HEADER
  36#endif                          /* PRINTK_HEADER */
  37#define PRINTK_HEADER DASD_NAME"(fba):"
  38
  39#define DASD_FBA_CCW_WRITE 0x41
  40#define DASD_FBA_CCW_READ 0x42
  41#define DASD_FBA_CCW_LOCATE 0x43
  42#define DASD_FBA_CCW_DEFINE_EXTENT 0x63
  43
  44#ifdef MODULE
  45#if (LINUX_VERSION_CODE >= KERNEL_VERSION(2,4,12))
  46MODULE_LICENSE("GPL");
  47#endif
  48#endif
  49
  50dasd_discipline_t dasd_fba_discipline;
  51
  52typedef struct
  53    dasd_fba_private_t {
  54        dasd_fba_characteristics_t rdc_data;
  55} dasd_fba_private_t;
  56
  57#ifdef CONFIG_DASD_DYNAMIC
  58static
  59devreg_t dasd_fba_known_devices[] = {
  60        {
  61              ci: { hc: {ctype:0x6310, dtype:0x9336}},
  62              flag:(DEVREG_MATCH_CU_TYPE |
  63                    DEVREG_MATCH_DEV_TYPE | DEVREG_TYPE_DEVCHARS),
  64              oper_func:dasd_oper_handler
  65        },
  66        {
  67                ci: { hc: {ctype:0x3880, dtype:0x3370}},
  68                flag:(DEVREG_MATCH_CU_TYPE |
  69                      DEVREG_MATCH_DEV_TYPE | DEVREG_TYPE_DEVCHARS),
  70                oper_func:dasd_oper_handler
  71        }
  72};
  73#endif
  74static inline int
  75define_extent (ccw1_t * ccw, DE_fba_data_t * DE_data, int rw,
  76               int blksize, int beg, int nr, ccw_req_t* cqr,
  77               dasd_device_t* device)
  78{
  79        int rc=0;
  80        memset (DE_data, 0, sizeof (DE_fba_data_t));
  81        ccw->cmd_code = DASD_FBA_CCW_DEFINE_EXTENT;
  82        ccw->count = 16;
  83        if ((rc=dasd_set_normalized_cda (ccw, __pa (DE_data), cqr, device)))
  84                return rc;
  85        if (rw == WRITE)
  86                (DE_data->mask).perm = 0x0;
  87        else if (rw == READ)
  88                (DE_data->mask).perm = 0x1;
  89        else
  90                DE_data->mask.perm = 0x2;
  91        DE_data->blk_size = blksize;
  92        DE_data->ext_loc = beg;
  93        DE_data->ext_end = nr - 1;
  94        return rc;
  95}
  96
  97static inline void
  98locate_record (ccw1_t * ccw, LO_fba_data_t * LO_data, int rw, int block_nr,
  99               int block_ct, ccw_req_t* cqr, dasd_device_t* device)
 100{
 101        memset (LO_data, 0, sizeof (LO_fba_data_t));
 102        ccw->cmd_code = DASD_FBA_CCW_LOCATE;
 103        ccw->count = 8;
 104        dasd_set_normalized_cda (ccw, __pa (LO_data), cqr, device);
 105        if (rw == WRITE)
 106                LO_data->operation.cmd = 0x5;
 107        else if (rw == READ)
 108                LO_data->operation.cmd = 0x6;
 109        else
 110                LO_data->operation.cmd = 0x8;
 111        LO_data->blk_nr = block_nr;
 112        LO_data->blk_ct = block_ct;
 113}
 114
 115static int
 116dasd_fba_id_check (s390_dev_info_t * info)
 117{
 118        if (info->sid_data.cu_type == 0x3880)
 119                if (info->sid_data.dev_type == 0x3370)
 120                        return 0;
 121        if (info->sid_data.cu_type == 0x6310)
 122                if (info->sid_data.dev_type == 0x9336)
 123                        return 0;
 124        return -ENODEV;
 125}
 126
 127static int
 128dasd_fba_check_characteristics (struct dasd_device_t *device)
 129{
 130        int rc = -ENODEV;
 131        void *rdc_data;
 132        dasd_fba_private_t *private;
 133
 134        if (device == NULL) {
 135
 136                MESSAGE (KERN_WARNING, "%s",
 137                         "Null device pointer passed to characteristics "
 138                         "checker");
 139
 140                return -ENODEV;
 141        }
 142        device->private = kmalloc (sizeof (dasd_fba_private_t), GFP_KERNEL);
 143
 144        if (device->private == NULL) {
 145
 146                MESSAGE (KERN_WARNING, "%s",
 147                        "memory allocation failed for private data");
 148
 149                rc = -ENOMEM;
 150                goto fail;
 151        }
 152        private = (dasd_fba_private_t *) device->private;
 153        rdc_data = (void *) &(private->rdc_data);
 154        rc = read_dev_chars (device->devinfo.irq, &rdc_data, 32);
 155
 156        if (rc) {
 157
 158                MESSAGE (KERN_WARNING,
 159                        "Read device characteristics returned error %d", 
 160                         rc);
 161
 162                goto fail;
 163        }
 164
 165        DEV_MESSAGE (KERN_INFO, device,
 166                     "%04X/%02X(CU:%04X/%02X) %dMB at(%d B/blk)",
 167                     device->devinfo.sid_data.dev_type,
 168                     device->devinfo.sid_data.dev_model,
 169                     device->devinfo.sid_data.cu_type,
 170                     device->devinfo.sid_data.cu_model,
 171                     ((private->rdc_data.blk_bdsa *
 172                       (private->rdc_data.blk_size >> 9)) >> 11),
 173                     private->rdc_data.blk_size);
 174
 175        goto out;
 176 fail:
 177        if ( rc ) {
 178                kfree(device->private);
 179                device->private = NULL;
 180        }
 181        
 182 out:
 183        return rc;
 184}
 185
 186static int
 187dasd_fba_do_analysis (struct dasd_device_t *device)
 188{
 189        int rc = 0;
 190        int sb;
 191        dasd_fba_private_t *private = (dasd_fba_private_t *) device->private;
 192        int bs = private->rdc_data.blk_size;
 193
 194        memset (&(device->sizes), 0, sizeof (dasd_sizes_t));
 195        switch (bs) {
 196        case 512:
 197        case 1024:
 198        case 2048:
 199        case 4096:
 200                device->sizes.bp_block = bs;
 201                break;
 202        default:
 203
 204                DEV_MESSAGE (KERN_INFO, device,
 205                             "unknown blocksize %d",
 206                             bs);
 207
 208                return -EMEDIUMTYPE;
 209        }
 210        device->sizes.s2b_shift = 0;    /* bits to shift 512 to get a block */
 211        for (sb = 512; sb < bs; sb = sb << 1)
 212                device->sizes.s2b_shift++;
 213
 214        device->sizes.blocks = (private->rdc_data.blk_bdsa);
 215        device->sizes.pt_block = 1;
 216
 217        return rc;
 218}
 219
 220static int
 221dasd_fba_fill_geometry (struct dasd_device_t *device, struct hd_geometry *geo)
 222{
 223        int rc = 0;
 224        unsigned long sectors = device->sizes.blocks << device->sizes.s2b_shift;
 225        unsigned long tracks = sectors >> 6;
 226        unsigned long cyls = tracks >> 4;
 227
 228        switch (device->sizes.bp_block) {
 229        case 512:
 230        case 1024:
 231        case 2048:
 232        case 4096:
 233                break;
 234        default:
 235                return -EINVAL;
 236        }
 237        geo->cylinders = cyls;
 238        geo->heads = 16;
 239        geo->sectors = 128 >> device->sizes.s2b_shift;
 240        return rc;
 241}
 242
 243static dasd_era_t
 244dasd_fba_examine_error (ccw_req_t * cqr, devstat_t * stat)
 245{
 246        dasd_device_t *device = (dasd_device_t *) cqr->device;
 247        if (stat->cstat == 0x00 &&
 248            stat->dstat == (DEV_STAT_CHN_END | DEV_STAT_DEV_END))
 249                    return dasd_era_none;
 250
 251        switch (device->devinfo.sid_data.dev_model) {
 252        case 0x3370:
 253                return dasd_3370_erp_examine (cqr, stat);
 254        case 0x9336:
 255                return dasd_9336_erp_examine (cqr, stat);
 256        default:
 257                return dasd_era_recover;
 258        }
 259}
 260
 261static dasd_erp_action_fn_t
 262dasd_fba_erp_action (ccw_req_t * cqr)
 263{
 264        return dasd_default_erp_action;
 265}
 266
 267static dasd_erp_postaction_fn_t
 268dasd_fba_erp_postaction (ccw_req_t * cqr)
 269{
 270        if (cqr->function == dasd_default_erp_action)
 271                return dasd_default_erp_postaction;
 272
 273        MESSAGE (KERN_WARNING,
 274                 "unknown ERP action %p", 
 275                 cqr->function);
 276
 277        return NULL;
 278}
 279
 280static ccw_req_t *
 281dasd_fba_build_cp_from_req (dasd_device_t * device, struct request *req)
 282{
 283        ccw_req_t *rw_cp = NULL;
 284        int rw_cmd;
 285        int bhct, i = 0;
 286        long size;
 287        ccw1_t *ccw;
 288        DE_fba_data_t *DE_data;
 289        LO_fba_data_t *LO_data;
 290        struct buffer_head *bh;
 291        dasd_fba_private_t *private = (dasd_fba_private_t *) device->private;
 292        int byt_per_blk = device->sizes.bp_block;
 293        unsigned long reloc_sector = req->sector + 
 294                device->major_info->gendisk.part[MINOR (req->rq_dev)].start_sect;
 295        
 296        if (req->cmd == READ) {
 297                rw_cmd = DASD_FBA_CCW_READ;
 298        } else if (req->cmd == WRITE) {
 299                rw_cmd = DASD_FBA_CCW_WRITE;
 300        } else {
 301
 302                MESSAGE (KERN_ERR,
 303                         "Unknown command %d\n", 
 304                         req->cmd);
 305
 306                return ERR_PTR(-EINVAL);
 307        }
 308        /* Build the request */
 309        /* count hs to prevent errors, when bh smaller than block */
 310        bh = req -> bh;
 311        bhct = 0;
 312        while ( bh != NULL ) {
 313                if (bh->b_size < byt_per_blk) {
 314                        BUG();
 315                }
 316                bhct += bh->b_size >> (device->sizes.s2b_shift+9);
 317                bh = bh->b_reqnext;
 318        }
 319        
 320        if (private->rdc_data.mode.bits.data_chain) {
 321                rw_cp = dasd_alloc_request (dasd_fba_discipline.name,
 322                                            2 + bhct,
 323                                            sizeof (DE_fba_data_t) +
 324                                            sizeof (LO_fba_data_t),
 325                                            device);
 326        } else {
 327                rw_cp = dasd_alloc_request (dasd_fba_discipline.name,
 328                                            1 + 2 * bhct,
 329                                            sizeof (DE_fba_data_t) +
 330                                            bhct * sizeof (LO_fba_data_t),
 331                                            device);
 332        }
 333        if (!rw_cp) {
 334                return ERR_PTR(-ENOMEM);
 335        }
 336        DE_data = rw_cp->data;
 337        LO_data = rw_cp->data + sizeof (DE_fba_data_t);
 338        ccw = rw_cp->cpaddr;
 339
 340        if (define_extent (ccw, DE_data, req->cmd, byt_per_blk,
 341                           reloc_sector, req->nr_sectors, rw_cp, device)) {
 342                goto clear_rw_cp;
 343        }
 344        ccw->flags |= CCW_FLAG_CC;
 345        ccw ++;
 346        locate_record (ccw, LO_data, req->cmd, 0, 
 347                       private->rdc_data.mode.bits.data_chain ? bhct : 1, rw_cp, device);
 348        if (ccw->cda == 0) {
 349                goto clear_rw_cp;
 350        }
 351        ccw->flags |= CCW_FLAG_CC;
 352        
 353        bh = req -> bh;
 354        i = 0;
 355        while ( bh != NULL ) {
 356                for (size = 0; size < bh->b_size; size += byt_per_blk) {
 357                        ccw ++;
 358                        ccw->cmd_code = rw_cmd;
 359                        ccw->count = byt_per_blk;
 360                        if (dasd_set_normalized_cda (ccw,__pa (bh->b_data + size), rw_cp, device)) {
 361                                goto clear_rw_cp;
 362                        }
 363                        if (private->rdc_data.mode.bits.data_chain) {
 364                                ccw->flags |= CCW_FLAG_DC;
 365                        } else {
 366                                ccw->flags |= CCW_FLAG_CC;
 367                        }
 368                }
 369                bh = bh->b_reqnext;
 370                if ( bh != NULL &&
 371                     !(private->rdc_data.mode.bits.data_chain)) {
 372                        ccw++;
 373                        i++;
 374                        LO_data++;
 375                        locate_record (ccw, LO_data, req->cmd, i, 1, rw_cp, device);
 376                        if (ccw->cda == 0) {
 377                                goto clear_rw_cp;
 378                        }
 379                        ccw->flags |= CCW_FLAG_CC;
 380                }
 381        }
 382        ccw->flags &= ~(CCW_FLAG_DC | CCW_FLAG_CC);
 383
 384        rw_cp->device = device;
 385        rw_cp->expires = 5 * TOD_MIN;           /* 5 minutes */
 386        rw_cp->req = req;
 387        check_then_set (&rw_cp->status, CQR_STATUS_EMPTY, CQR_STATUS_FILLED);
 388        goto out;
 389 clear_rw_cp:
 390        dasd_free_request (rw_cp, device);
 391        rw_cp = NULL;
 392 out:
 393        return rw_cp;
 394}
 395
 396static int
 397dasd_fba_fill_info (dasd_device_t * device, dasd_information2_t * info)
 398{
 399        int rc = 0;
 400        info->label_block = 1;
 401        info->FBA_layout = 1;
 402        info->format = DASD_FORMAT_LDL;
 403        info->characteristics_size = sizeof (dasd_fba_characteristics_t);
 404        memcpy (info->characteristics,
 405                &((dasd_fba_private_t *) device->private)->rdc_data,
 406                sizeof (dasd_fba_characteristics_t));
 407        info->confdata_size = 0;
 408        return rc;
 409}
 410
 411
 412dasd_discipline_t dasd_fba_discipline = {
 413        owner: THIS_MODULE,
 414        name:"FBA ",
 415        ebcname:"FBA ",
 416        max_blocks:((PAGE_SIZE >> 1) / sizeof (ccw1_t) - 1),
 417        id_check:dasd_fba_id_check,
 418        check_characteristics:dasd_fba_check_characteristics,
 419        do_analysis:dasd_fba_do_analysis,
 420        fill_geometry:dasd_fba_fill_geometry,
 421        start_IO:dasd_start_IO,
 422        term_IO:dasd_term_IO,
 423        examine_error:dasd_fba_examine_error,
 424        erp_action:dasd_fba_erp_action,
 425        erp_postaction:dasd_fba_erp_postaction,
 426        build_cp_from_req:dasd_fba_build_cp_from_req,
 427        int_handler:dasd_int_handler,
 428        fill_info:dasd_fba_fill_info,
 429        list:LIST_HEAD_INIT(dasd_fba_discipline.list),
 430};
 431
 432int
 433dasd_fba_init (void)
 434{
 435        int rc = 0;
 436
 437        MESSAGE (KERN_INFO,
 438                 "%s discipline initializing", 
 439                 dasd_fba_discipline.name);
 440
 441        ASCEBC (dasd_fba_discipline.ebcname, 4);
 442        dasd_discipline_add (&dasd_fba_discipline);
 443#ifdef CONFIG_DASD_DYNAMIC
 444        {
 445                int i;
 446                for (i = 0;
 447                     i < sizeof (dasd_fba_known_devices) / sizeof (devreg_t);
 448                     i++) {
 449
 450                        MESSAGE (KERN_INFO,
 451                                 "We are interested in: "
 452                                 "Dev %04X/%02X @ CU %04X/%02x",
 453                                 dasd_fba_known_devices[i].ci.hc.dtype,
 454                                 dasd_fba_known_devices[i].ci.hc.dmode,
 455                                 dasd_fba_known_devices[i].ci.hc.ctype,
 456                                 dasd_fba_known_devices[i].ci.hc.cmode);
 457
 458                        s390_device_register (&dasd_fba_known_devices[i]);
 459                }
 460        }
 461#endif                          /* CONFIG_DASD_DYNAMIC */
 462        return rc;
 463}
 464
 465void
 466dasd_fba_cleanup( void ) {
 467
 468        MESSAGE (KERN_INFO,
 469                 "%s discipline cleaning up", 
 470                 dasd_fba_discipline.name);
 471
 472#ifdef CONFIG_DASD_DYNAMIC
 473        {
 474        int i;
 475        for ( i=0; i<sizeof(dasd_fba_known_devices)/sizeof(devreg_t); i++) {
 476                s390_device_unregister(&dasd_fba_known_devices[i]);
 477        }
 478        }
 479#endif /* CONFIG_DASD_DYNAMIC */
 480        dasd_discipline_del(&dasd_fba_discipline);
 481}
 482
 483#ifdef MODULE
 484int
 485init_module (void)
 486{
 487        int rc = 0;
 488        rc = dasd_fba_init ();
 489        return rc;
 490}
 491
 492void
 493cleanup_module (void)
 494{
 495        dasd_fba_cleanup ();
 496        return;
 497}
 498#endif
 499
 500
 501/*
 502 * Overrides for Emacs so that we follow Linus's tabbing style.
 503 * Emacs will notice this stuff at the end of the file and automatically
 504 * adjust the settings for this buffer only.  This must remain at the end
 505 * of the file.
 506 * ---------------------------------------------------------------------------
 507 * Local variables:
 508 * c-indent-level: 4 
 509 * c-brace-imaginary-offset: 0
 510 * c-brace-offset: -4
 511 * c-argdecl-indent: 4
 512 * c-label-offset: -4
 513 * c-continued-statement-offset: 4
 514 * c-continued-brace-offset: 0
 515 * indent-tabs-mode: nil
 516 * tab-width: 8
 517 * End:
 518 */
 519
lxr.linux.no kindly hosted by Redpill Linpro AS, provider of Linux consulting and operations services since 1995.