/* * scsi_dev_info_list: structure to hold black/white listed devices.
*/ struct scsi_dev_info_list { struct list_head dev_info_list; char vendor[8]; char model[16];
blist_flags_t flags; unsigned compatible; /* for use with scsi_static_device_list entries */
};
struct scsi_dev_info_list_table { struct list_head node; /* our node for being on the master list */ struct list_head scsi_dev_info_list; /* head of dev info list */ constchar *name; /* name of list for /proc (NULL for global) */ int key; /* unique numeric identifier */
};
list_for_each_entry(devinfo_table, &scsi_dev_info_list, node) if (devinfo_table->key == key) return devinfo_table;
return ERR_PTR(-EINVAL);
}
/* * scsi_strcpy_devinfo: called from scsi_dev_info_list_add to copy into * devinfo vendor and model strings.
*/ staticvoid scsi_strcpy_devinfo(char *name, char *to, size_t to_length, char *from, int compatible)
{
size_t from_length;
from_length = strlen(from);
/* * null pad and null terminate if compatible * otherwise space pad
*/ if (compatible)
strscpy_pad(to, from, to_length); else
memcpy_and_pad(to, to_length, from, from_length, ' ');
if (from_length > to_length)
printk(KERN_WARNING "%s: %s string '%s' is too long\n",
__func__, name, from);
}
/** * scsi_dev_info_list_add - add one dev_info list entry. * @compatible: if true, null terminate short strings. Otherwise space pad. * @vendor: vendor string * @model: model (product) string * @strflags: integer string * @flags: if strflags NULL, use this flag value * * Description: * Create and add one dev_info entry for @vendor, @model, @strflags or * @flag. If @compatible, add to the tail of the list, do not space * pad, and set devinfo->compatible. The scsi_static_device_list entries * are added with @compatible 1 and @clfags NULL. * * Returns: 0 OK, -error on failure.
**/ staticint scsi_dev_info_list_add(int compatible, char *vendor, char *model, char *strflags, blist_flags_t flags)
{ return scsi_dev_info_list_add_keyed(compatible, vendor, model,
strflags, flags,
SCSI_DEVINFO_GLOBAL);
}
/** * scsi_dev_info_list_add_keyed - add one dev_info list entry. * @compatible: if true, null terminate short strings. Otherwise space pad. * @vendor: vendor string * @model: model (product) string * @strflags: integer string * @flags: if strflags NULL, use this flag value * @key: specify list to use * * Description: * Create and add one dev_info entry for @vendor, @model, * @strflags or @flag in list specified by @key. If @compatible, * add to the tail of the list, do not space pad, and set * devinfo->compatible. The scsi_static_device_list entries are * added with @compatible 1 and @clfags NULL. * * Returns: 0 OK, -error on failure.
**/ int scsi_dev_info_list_add_keyed(int compatible, char *vendor, char *model, char *strflags, blist_flags_t flags, enum scsi_devinfo_key key)
{ struct scsi_dev_info_list *devinfo; struct scsi_dev_info_list_table *devinfo_table =
scsi_devinfo_lookup_by_key(key);
if (IS_ERR(devinfo_table)) return PTR_ERR(devinfo_table);
devinfo = kmalloc(sizeof(*devinfo), GFP_KERNEL); if (!devinfo) {
printk(KERN_ERR "%s: no memory\n", __func__); return -ENOMEM;
}
/** * scsi_dev_info_list_find - find a matching dev_info list entry. * @vendor: full vendor string * @model: full model (product) string * @key: specify list to use * * Description: * Finds the first dev_info entry matching @vendor, @model * in list specified by @key. * * Returns: pointer to matching entry, or ERR_PTR on failure.
**/ staticstruct scsi_dev_info_list *scsi_dev_info_list_find(constchar *vendor, constchar *model, enum scsi_devinfo_key key)
{ struct scsi_dev_info_list *devinfo; struct scsi_dev_info_list_table *devinfo_table =
scsi_devinfo_lookup_by_key(key);
size_t vmax, mmax, mlen; constchar *vskip, *mskip;
if (IS_ERR(devinfo_table)) return (struct scsi_dev_info_list *) devinfo_table;
/* Prepare for "compatible" matches */
/* * XXX why skip leading spaces? If an odd INQUIRY * value, that should have been part of the * scsi_static_device_list[] entry, such as " FOO" * rather than "FOO". Since this code is already * here, and we don't know what device it is * trying to work with, leave it as-is.
*/
vmax = sizeof(devinfo->vendor);
vskip = vendor; while (vmax > 0 && *vskip == ' ') {
vmax--;
vskip++;
} /* Also skip trailing spaces */ while (vmax > 0 && vskip[vmax - 1] == ' ')
--vmax;
list_for_each_entry(devinfo, &devinfo_table->scsi_dev_info_list,
dev_info_list) { if (devinfo->compatible) { /* * vendor strings must be an exact match
*/ if (vmax != strnlen(devinfo->vendor, sizeof(devinfo->vendor)) ||
memcmp(devinfo->vendor, vskip, vmax)) continue;
/* * @model specifies the full string, and * must be larger or equal to devinfo->model
*/
mlen = strnlen(devinfo->model, sizeof(devinfo->model)); if (mmax < mlen || memcmp(devinfo->model, mskip, mlen)) continue; return devinfo;
} else { if (!memcmp(devinfo->vendor, vendor, sizeof(devinfo->vendor)) &&
!memcmp(devinfo->model, model, sizeof(devinfo->model))) return devinfo;
}
}
return ERR_PTR(-ENOENT);
}
/** * scsi_dev_info_list_add_str - parse dev_list and add to the scsi_dev_info_list. * @dev_list: string of device flags to add * * Description: * Parse dev_list, and add entries to the scsi_dev_info_list. * dev_list is of the form "vendor:product:flag,vendor:product:flag". * dev_list is modified via strsep. Can be called for command line * addition, for proc or mabye a sysfs interface. * * Returns: 0 if OK, -error on failure.
**/ staticint scsi_dev_info_list_add_str(char *dev_list)
{ char *vendor, *model, *strflags, *next; char *next_check; int res = 0;
next = dev_list; if (next && next[0] == '"') { /* * Ignore both the leading and trailing quote.
*/
next++;
next_check = ",\"";
} else {
next_check = ",";
}
/* * For the leading and trailing '"' case, the for loop comes * through the last time with vendor[0] == '\0'.
*/ for (vendor = strsep(&next, ":"); vendor && (vendor[0] != '\0')
&& (res == 0); vendor = strsep(&next, ":")) {
strflags = NULL;
model = strsep(&next, ":"); if (model)
strflags = strsep(&next, next_check); if (!model || !strflags) {
pr_err("%s: bad dev info string '%s' '%s' '%s'\n",
__func__, vendor, model ? model : "",
strflags ? strflags : "");
res = -EINVAL;
} else
res = scsi_dev_info_list_add(0 /* compatible */, vendor,
model, strflags, 0);
} return res;
}
/** * scsi_get_device_flags - get device specific flags from the dynamic * device list. * @sdev: &scsi_device to get flags for * @vendor: vendor name * @model: model name * * Description: * Search the global scsi_dev_info_list (specified by list zero) * for an entry matching @vendor and @model, if found, return the * matching flags value, else return the host or global default * settings. Called during scan time.
**/
blist_flags_t scsi_get_device_flags(struct scsi_device *sdev, constunsignedchar *vendor, constunsignedchar *model)
{ return scsi_get_device_flags_keyed(sdev, vendor, model,
SCSI_DEVINFO_GLOBAL);
}
/** * scsi_get_device_flags_keyed - get device specific flags from the dynamic device list * @sdev: &scsi_device to get flags for * @vendor: vendor name * @model: model name * @key: list to look up * * Description: * Search the scsi_dev_info_list specified by @key for an entry * matching @vendor and @model, if found, return the matching * flags value, else return the host or global default settings. * Called during scan time.
**/
blist_flags_t scsi_get_device_flags_keyed(struct scsi_device *sdev, constunsignedchar *vendor, constunsignedchar *model, enum scsi_devinfo_key key)
{ struct scsi_dev_info_list *devinfo;
devinfo = scsi_dev_info_list_find(vendor, model, key); if (!IS_ERR(devinfo)) return devinfo->flags;
/* key or device not found: return nothing */ if (key != SCSI_DEVINFO_GLOBAL) return 0;
/* except for the global list, where we have an exception */ if (sdev->sdev_bflags) return sdev->sdev_bflags;
/* * proc_scsi_dev_info_write - allow additions to scsi_dev_info_list via /proc. * * Description: Adds a black/white list entry for vendor and model with an * integer value of flag to the scsi device info list. * To use, echo "vendor:model:flag" > /proc/scsi/device_info
*/ static ssize_t proc_scsi_devinfo_write(struct file *file, constchar __user *buf,
size_t length, loff_t *ppos)
{ char *buffer;
ssize_t err = length;
if (!buf || length>PAGE_SIZE) return -EINVAL; if (!(buffer = (char *) __get_free_page(GFP_KERNEL))) return -ENOMEM; if (copy_from_user(buffer, buf, length)) {
err =-EFAULT; goto out;
}
module_param_string(dev_flags, scsi_dev_flags, sizeof(scsi_dev_flags), 0);
MODULE_PARM_DESC(dev_flags, "Given scsi_dev_flags=vendor:model:flags[,v:m:f] add black/white" " list entries for vendor and model with an integer value of flags" " to the scsi device info list");
/** * scsi_dev_info_add_list - add a new devinfo list * @key: key of the list to add * @name: Name of the list to add (for /proc/scsi/device_info) * * Adds the requested list, returns zero on success, -EEXIST if the * key is already registered to a list, or other error on failure.
*/ int scsi_dev_info_add_list(enum scsi_devinfo_key key, constchar *name)
{ struct scsi_dev_info_list_table *devinfo_table =
scsi_devinfo_lookup_by_key(key);
if (!IS_ERR(devinfo_table)) /* list already exists */ return -EEXIST;
/** * scsi_dev_info_remove_list - destroy an added devinfo list * @key: key of the list to destroy * * Iterates over the entire list first, freeing all the values, then * frees the list itself. Returns 0 on success or -EINVAL if the key * can't be found.
*/ int scsi_dev_info_remove_list(enum scsi_devinfo_key key)
{ struct list_head *lh, *lh_next; struct scsi_dev_info_list_table *devinfo_table =
scsi_devinfo_lookup_by_key(key);
if (IS_ERR(devinfo_table)) /* no such list */ return -EINVAL;
/* remove from the master list */
list_del(&devinfo_table->node);
/** * scsi_init_devinfo - set up the dynamic device list. * * Description: * Add command line entries from scsi_dev_flags, then add * scsi_static_device_list entries to the scsi device info list.
*/ int __init scsi_init_devinfo(void)
{ #ifdef CONFIG_SCSI_PROC_FS struct proc_dir_entry *p; #endif int error, i;
error = scsi_dev_info_add_list(SCSI_DEVINFO_GLOBAL, NULL); if (error) return error;
error = scsi_dev_info_list_add_str(scsi_dev_flags); if (error) goto out;
for (i = 0; scsi_static_device_list[i].vendor; i++) {
error = scsi_dev_info_list_add(1 /* compatible */,
scsi_static_device_list[i].vendor,
scsi_static_device_list[i].model,
NULL,
scsi_static_device_list[i].flags); if (error) goto out;
}
#ifdef CONFIG_SCSI_PROC_FS
p = proc_create("scsi/device_info", 0, NULL, &scsi_devinfo_proc_ops); if (!p) {
error = -ENOMEM; goto out;
} #endif/* CONFIG_SCSI_PROC_FS */
out: if (error)
scsi_exit_devinfo(); return error;
}
Messung V0.5 in Prozent
¤ Dauer der Verarbeitung: 0.20 Sekunden
(vorverarbeitet am 2026-04-29)
¤
Die Informationen auf dieser Webseite wurden
nach bestem Wissen sorgfältig zusammengestellt. Es wird jedoch weder Vollständigkeit, noch Richtigkeit,
noch Qualität der bereit gestellten Informationen zugesichert.
Bemerkung:
Die farbliche Syntaxdarstellung und die Messung sind noch experimentell.