| .. | .. |
|---|
| 1 | +// SPDX-License-Identifier: GPL-2.0-only |
|---|
| 1 | 2 | /* |
|---|
| 2 | 3 | * SCSI Media Changer device driver for Linux 2.6 |
|---|
| 3 | 4 | * |
|---|
| .. | .. |
|---|
| 43 | 44 | MODULE_ALIAS_CHARDEV_MAJOR(SCSI_CHANGER_MAJOR); |
|---|
| 44 | 45 | MODULE_ALIAS_SCSI_DEVICE(TYPE_MEDIUM_CHANGER); |
|---|
| 45 | 46 | |
|---|
| 46 | | -static DEFINE_MUTEX(ch_mutex); |
|---|
| 47 | 47 | static int init = 1; |
|---|
| 48 | 48 | module_param(init, int, 0444); |
|---|
| 49 | 49 | MODULE_PARM_DESC(init, \ |
|---|
| .. | .. |
|---|
| 568 | 568 | { |
|---|
| 569 | 569 | scsi_changer *ch = container_of(ref, scsi_changer, ref); |
|---|
| 570 | 570 | |
|---|
| 571 | + ch->device = NULL; |
|---|
| 571 | 572 | kfree(ch->dt); |
|---|
| 572 | 573 | kfree(ch); |
|---|
| 573 | 574 | } |
|---|
| .. | .. |
|---|
| 589 | 590 | scsi_changer *ch; |
|---|
| 590 | 591 | int minor = iminor(inode); |
|---|
| 591 | 592 | |
|---|
| 592 | | - mutex_lock(&ch_mutex); |
|---|
| 593 | 593 | spin_lock(&ch_index_lock); |
|---|
| 594 | 594 | ch = idr_find(&ch_index_idr, minor); |
|---|
| 595 | 595 | |
|---|
| 596 | | - if (NULL == ch || scsi_device_get(ch->device)) { |
|---|
| 596 | + if (ch == NULL || !kref_get_unless_zero(&ch->ref)) { |
|---|
| 597 | 597 | spin_unlock(&ch_index_lock); |
|---|
| 598 | | - mutex_unlock(&ch_mutex); |
|---|
| 599 | 598 | return -ENXIO; |
|---|
| 600 | 599 | } |
|---|
| 601 | | - kref_get(&ch->ref); |
|---|
| 602 | 600 | spin_unlock(&ch_index_lock); |
|---|
| 603 | | - |
|---|
| 601 | + if (scsi_device_get(ch->device)) { |
|---|
| 602 | + kref_put(&ch->ref, ch_destroy); |
|---|
| 603 | + return -ENXIO; |
|---|
| 604 | + } |
|---|
| 605 | + /* Synchronize with ch_probe() */ |
|---|
| 606 | + mutex_lock(&ch->lock); |
|---|
| 604 | 607 | file->private_data = ch; |
|---|
| 605 | | - mutex_unlock(&ch_mutex); |
|---|
| 608 | + mutex_unlock(&ch->lock); |
|---|
| 606 | 609 | return 0; |
|---|
| 607 | 610 | } |
|---|
| 608 | 611 | |
|---|
| .. | .. |
|---|
| 871 | 874 | unsigned int cmd, unsigned long arg) |
|---|
| 872 | 875 | { |
|---|
| 873 | 876 | scsi_changer *ch = file->private_data; |
|---|
| 877 | + int retval = scsi_ioctl_block_when_processing_errors(ch->device, cmd, |
|---|
| 878 | + file->f_flags & O_NDELAY); |
|---|
| 879 | + if (retval) |
|---|
| 880 | + return retval; |
|---|
| 874 | 881 | |
|---|
| 875 | 882 | switch (cmd) { |
|---|
| 876 | 883 | case CHIOGPARAMS: |
|---|
| .. | .. |
|---|
| 882 | 889 | case CHIOINITELEM: |
|---|
| 883 | 890 | case CHIOSVOLTAG: |
|---|
| 884 | 891 | /* compatible */ |
|---|
| 885 | | - return ch_ioctl(file, cmd, arg); |
|---|
| 892 | + return ch_ioctl(file, cmd, (unsigned long)compat_ptr(arg)); |
|---|
| 886 | 893 | case CHIOGSTATUS32: |
|---|
| 887 | 894 | { |
|---|
| 888 | 895 | struct changer_element_status32 ces32; |
|---|
| .. | .. |
|---|
| 897 | 904 | return ch_gstatus(ch, ces32.ces_type, data); |
|---|
| 898 | 905 | } |
|---|
| 899 | 906 | default: |
|---|
| 900 | | - // return scsi_ioctl_compat(ch->device, cmd, (void*)arg); |
|---|
| 901 | | - return -ENOIOCTLCMD; |
|---|
| 907 | + return scsi_compat_ioctl(ch->device, cmd, compat_ptr(arg)); |
|---|
| 902 | 908 | |
|---|
| 903 | 909 | } |
|---|
| 904 | 910 | } |
|---|
| .. | .. |
|---|
| 934 | 940 | |
|---|
| 935 | 941 | ch->minor = ret; |
|---|
| 936 | 942 | sprintf(ch->name,"ch%d",ch->minor); |
|---|
| 943 | + ret = scsi_device_get(sd); |
|---|
| 944 | + if (ret) { |
|---|
| 945 | + sdev_printk(KERN_WARNING, sd, "ch%d: failed to get device\n", |
|---|
| 946 | + ch->minor); |
|---|
| 947 | + goto remove_idr; |
|---|
| 948 | + } |
|---|
| 937 | 949 | |
|---|
| 950 | + mutex_init(&ch->lock); |
|---|
| 951 | + kref_init(&ch->ref); |
|---|
| 952 | + ch->device = sd; |
|---|
| 938 | 953 | class_dev = device_create(ch_sysfs_class, dev, |
|---|
| 939 | 954 | MKDEV(SCSI_CHANGER_MAJOR, ch->minor), ch, |
|---|
| 940 | 955 | "s%s", ch->name); |
|---|
| .. | .. |
|---|
| 942 | 957 | sdev_printk(KERN_WARNING, sd, "ch%d: device_create failed\n", |
|---|
| 943 | 958 | ch->minor); |
|---|
| 944 | 959 | ret = PTR_ERR(class_dev); |
|---|
| 945 | | - goto remove_idr; |
|---|
| 960 | + goto put_device; |
|---|
| 946 | 961 | } |
|---|
| 947 | 962 | |
|---|
| 948 | | - mutex_init(&ch->lock); |
|---|
| 949 | | - kref_init(&ch->ref); |
|---|
| 950 | | - ch->device = sd; |
|---|
| 963 | + mutex_lock(&ch->lock); |
|---|
| 951 | 964 | ret = ch_readconfig(ch); |
|---|
| 952 | | - if (ret) |
|---|
| 965 | + if (ret) { |
|---|
| 966 | + mutex_unlock(&ch->lock); |
|---|
| 953 | 967 | goto destroy_dev; |
|---|
| 968 | + } |
|---|
| 954 | 969 | if (init) |
|---|
| 955 | 970 | ch_init_elem(ch); |
|---|
| 956 | 971 | |
|---|
| 972 | + mutex_unlock(&ch->lock); |
|---|
| 957 | 973 | dev_set_drvdata(dev, ch); |
|---|
| 958 | 974 | sdev_printk(KERN_INFO, sd, "Attached scsi changer %s\n", ch->name); |
|---|
| 959 | 975 | |
|---|
| 960 | 976 | return 0; |
|---|
| 961 | 977 | destroy_dev: |
|---|
| 962 | 978 | device_destroy(ch_sysfs_class, MKDEV(SCSI_CHANGER_MAJOR, ch->minor)); |
|---|
| 979 | +put_device: |
|---|
| 980 | + scsi_device_put(sd); |
|---|
| 963 | 981 | remove_idr: |
|---|
| 964 | 982 | idr_remove(&ch_index_idr, ch->minor); |
|---|
| 965 | 983 | free_ch: |
|---|
| .. | .. |
|---|
| 973 | 991 | |
|---|
| 974 | 992 | spin_lock(&ch_index_lock); |
|---|
| 975 | 993 | idr_remove(&ch_index_idr, ch->minor); |
|---|
| 994 | + dev_set_drvdata(dev, NULL); |
|---|
| 976 | 995 | spin_unlock(&ch_index_lock); |
|---|
| 977 | 996 | |
|---|
| 978 | 997 | device_destroy(ch_sysfs_class, MKDEV(SCSI_CHANGER_MAJOR,ch->minor)); |
|---|
| 998 | + scsi_device_put(ch->device); |
|---|
| 979 | 999 | kref_put(&ch->ref, ch_destroy); |
|---|
| 980 | 1000 | return 0; |
|---|
| 981 | 1001 | } |
|---|